如何最有效地处理大量文件描述符?

7
有几种选项可用于处理大量套接字连接的程序(例如Web服务、P2P系统等)。
  1. 为每个套接字生成一个单独的线程来处理I/O。
  2. 使用select系统调用将I/O多路复用到单个线程中。
  3. 使用poll系统调用将I/O多路复用(替换select)。
  4. 使用epoll系统调用,避免反复通过用户/系统边界发送套接字fd。
  5. 生成若干个I/O线程,每个线程使用poll API多路复用相对较小的一组总连接数。
  6. 与#5类似,但使用epoll API为每个独立的I/O线程创建一个单独的epoll对象。
在多核CPU上,我希望#5或#6能够提供最佳性能,但我没有任何硬数据支持。在网上搜索时,发现this页面描述了作者测试上述方法#2、#3和#4的经验。不幸的是,这个网页似乎已经有7年左右没有明显的更新了。
因此,我的问题是哪种方法被人们发现最有效,或者是否有其他方法比上面列出的任何一种更好?对实际生活中的图表、白皮书和/或网络可用的写作的参考将不胜感激。

我认为这是一个已经解决的问题,答案在这里 - http://www.kegel.com/c10k.html。 - computinglife
4个回答

3
根据我在运行大型IRC服务器方面的经验,我们曾使用select()和poll()(因为epoll()/kqueue()还不可用)。在大约700个同时客户端时,服务器将使用100%的CPU(IRC服务器没有多线程)。但有趣的是,服务器仍然表现良好。在大约4,000个客户端时,服务器开始出现延迟。
原因是,在大约700个客户端时,当我们返回select()时,将有一个可用于处理的客户端。扫描以查找它是哪个客户端的for()循环将耗费大部分CPU。随着我们获得更多客户端,我们将在每次调用select()中开始获得更多需要处理的客户端,因此我们变得更加高效。
使用epoll()/kqueue(),类似规格的机器可以轻松处理10,000个客户端,一些(尽管更强大的机器,但仍然被认为是今天标准下微小的机器)可以容纳30,000个客户端而无需费力。
我看到的关于SIGIO的实验似乎表明它适用于对延迟极为重要的应用程序,在那里只有少数活跃的客户端进行非常少量的单独工作。
我建议在几乎任何情况下都使用epoll()/kqueue()而不是select()/poll()。我没有尝试将客户端分成线程。老实说,我从未找到需要在前端客户端处理上进行更多优化的服务,以证明使用线程的实验。

2

根据我的经验,您可以使用#6获得最佳性能。

我还建议您研究libevent以处理一些抽象的细节。至少,您将能够查看一些基准测试结果benchmark results

此外,您需要处理多少个套接字?在您开始使用至少几百个套接字之前,您的方法可能并不重要。


2

我已经花费了过去两年的时间在解决这个具体问题上(为G-WAN web服务器,它带有许多基准测试和图表来显示所有这些)。

在Linux下表现最佳的模型是epoll,采用一个事件队列(并且对于重量级处理,采用多个工作线程)。

如果你的处理量很少(低处理延迟),那么使用一个线程比使用多个线程更快。

这是因为epoll无法在多核CPU上扩展(在同一用户模式应用程序中使用多个并发epoll队列进行连接I/O将使您的服务器变慢)。

我没有认真研究过内核中epoll的代码(到目前为止,我只关注了用户模式),但我的猜测是内核中的epoll实现被锁定了。

这就是为什么使用多个线程很快就会遇到瓶颈。

不言而喻,如果Linux想保持其作为最佳性能内核之一的地位,这种状况不应该持续存在。


0

我广泛使用epoll(),它的性能表现很好。我通常有数千个套接字处于活动状态,并且测试时最多使用131,072个套接字。而epoll()总是可以处理它们。

我使用多个线程,每个线程轮询一部分套接字。这使代码变得复杂,但充分利用了多核CPU的优势。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接