操作系统如何在接受同一个套接字的多个进程之间进行负载平衡?

Posted

技术标签:

【中文标题】操作系统如何在接受同一个套接字的多个进程之间进行负载平衡?【英文标题】:How does the operating system load balance between multiple processes accepting the same socket? 【发布时间】:2012-09-11 18:34:29 【问题描述】:

我正在阅读 Node.js 中集群模块的文档:http://nodejs.org/api/cluster.html

它声称以下内容:

当多个进程都accept()ing 在同一个底层时 资源,操作系统之间的负载平衡非常 高效。

这听起来很合理,但即使经过几个小时的谷歌搜索,我也没有找到任何文章或任何东西可以证实这一点,或者解释这种负载平衡逻辑在操作系统中的工作原理。

另外,什么操作系统在做这种有效的负载平衡?

【问题讨论】:

我猜你找不到任何相关的东西,因为 js 不应该依赖于操作系统。我不知道为什么该文档提到操作系统,也许它正在考虑将 Chrome 作为操作系统。 感兴趣的,citi.umich.edu/u/cel/linux-scalability/reports/accept.html @Paul - Node.js 是一个服务器端框架,它不在浏览器中运行。 @Steve 我在谷歌搜索时也发现了该链接,但它在旧版本的 Linux 内核中处理不同的问题。 研究 Apache 中使用的“接受互斥锁”ref:所有等待线程在每个新连接上唤醒。 【参考方案1】:

“负载平衡”可能是一个有点糟糕的词选择,本质上它只是操作系统如何选择唤醒和/或接下来运行的进程的问题。通常,进程调度程序会尝试根据标准选择要运行的进程,例如将相同份额的 cpu 时间分配给具有相同优先级的进程、cpu/内存位置(不要在 cpu 周围反弹进程)等。无论如何,通过谷歌搜索你会发现很多关于进程调度算法和实现的资料。

现在,对于 accept() 的特殊情况,这还取决于操作系统如何实现唤醒等待 accept() 的进程。

一个简单的实现是唤醒每个在 accept() 调用中阻塞的进程,然后让调度程序选择它们运行的​​顺序。

上述方法很简单,但会导致“雷声”问题,因为只有第一个进程成功接受连接,其他进程又回到阻塞状态。一种更复杂的方法是让操作系统只唤醒一个进程。这里可以通过询问调度程序来选择唤醒哪个进程,或者例如只需选择blocked-on-accept()-for-this-socket 列表中的第一个进程。后者是十多年前 Linux 所做的,基于其他人已经发布的 link。

请注意,这只适用于阻塞 accept();对于非阻塞accept()(我确定这是node.js正在做的事情),问题就在于select()/poll()/whatever中哪个进程阻塞了事件。 poll()/select() 的语义实际上要求它们都被唤醒,所以你又遇到了雷鸣般的羊群问题。对于 Linux,以及可能以类似方式具有系统特定高性能轮询接口的其他系统,可以通过使用单个共享 epoll fd 和边缘触发事件来避免雷鸣般的羊群。在这种情况下,事件将仅传递到 epoll_wait() 上阻塞的进程之一。我认为,与阻塞 accept() 类似,选择将事件传递到的进程只是为特定 epoll fd 选择在 epoll_wait() 上阻塞的进程列表中的第一个。

李>

所以至少对于 Linux 来说,无论是阻塞的 accept() 还是带有边缘触发 epoll 的非阻塞的 accept(),在选择唤醒哪个进程时都没有调度本身。但是 OTOH,无论如何,进程之间的工作负载可能会相当均衡,因为本质上系统将按照进程完成当前工作的顺序轮询进程,然后返回到 epoll_wait() 上的阻塞。

【讨论】:

太棒了,感谢您的解释。所以基本上,这是否意味着他们不实施任何负载平衡,而是信任内核通过将新连接提供给最不忙的进程来“做正确的事情”? 谢谢!这是一个很好的解释,并且与我们看到的在金属上运行的 2.6 Linux 内核非常匹配。但是,在使用 3.2 内核的 AWS 上运行时,我们看到了一些非常不同的东西——当然不是循环。您知道应该在哪里寻找内核之间的变化或虚拟化硬件上的不同性能吗? @Brett:抱歉,不知道。出于好奇,您在 AWS 上看到了什么?对于一个在黑暗中摸索的想法,我猜虚拟机主机如何选择要调度的虚拟机 CPU 可能会有一些差异? @janneb 我们在裸机上尝试了 3.2 内核,看到的结果与在 AWS 上相同。我在这里问了一个问题:***.com/questions/13770826/…,并在这里评论了一个 node.js 问题:github.com/joyent/node/issues/3241 请注意,在 v0.11 上,使用轮询负载平衡。

以上是关于操作系统如何在接受同一个套接字的多个进程之间进行负载平衡?的主要内容,如果未能解决你的问题,请参考以下文章

进程间的通信简单总结

辅助库可以用于在不相关的进程(不是分叉的进程)之间共享接受的连接吗?

有没有办法让多个进程共享一个监听套接字?

.Net 进程间通信

如何在同一个进程中操作多个 ZeroMQ Socket 类型?

可以比 IPC 或套接字更直接地在进程之间进行通信吗?