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

时间:2020-03-06 14:48:56  来源:igfitidea点击:

处理大量套接字连接的程序(例如Web服务,p2p系统等)似乎有几个选项可用。

  • 产生一个单独的线程来处理每个套接字的I / O。
  • 使用select系统调用将I / O多路复用到单个线程中。
  • 使用轮询系统调用来复用I / O(替换选择)。
  • 使用epoll系统调用来避免必须通过用户/系统边界重复发送套接字fd。
  • 产生许多I / O线程,每个线程都使用poll API多路复用连接总数中相对较小的一组。
  • 按照#5,除了使用epoll API为每个独立的I / O线程创建一个单独的epoll对象。

在多核CPU上,我希望#5或者#6具有最佳性能,但是我没有任何硬数据来支持这一点。在网上搜索后,此页面描述了作者测试上述#2,#3和#4方法的经验。不幸的是,该网页大约存在7年之久,没有发现明显的最新更新。

所以我的问题是,人们发现哪种方法最有效?和/或者还有没有一种方法比上面列出的方法更好?引用现实生活中的图表,白皮书和/或者网络上可用的内容将不胜感激。

解决方案

我广泛使用epoll(),并且效果很好。我通常有成千上万的套接字处于活动状态,并且最多可以测试131,072个套接字。而且epoll()始终可以处理它。

我使用多个线程,每个线程都在套接字的一个子集上轮询。这使代码复杂化,但充分利用了多核CPU。

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

我还建议我们研究libevent,以解决其中的一些细节。至少,我们将能够看到他们的一些基准。

另外,我们在谈论多少个插座?在开始获得至少数百个套接字之前,方法可能并没有太大关系。

以我运行大型IRC服务器的经验来看,我们曾经使用select()和poll()(因为epoll()/ kqueue()不可用)。在大约700个并发客户端上,服务器将使用100%的CPU(irc服务器不是多线程的)。但是,有趣的是服务器仍将运行良好。在大约4,000个客户端时,服务器将开始延迟。

原因是在大约700个客户端上,当我们返回到select()时,将有一个客户端可以处理。 for()循环扫描以找出哪个客户端将占用大部分CPU。随着我们拥有更多客户,我们将开始越来越多的客户在每次对select()的调用中都需要处理,因此我们将变得更有效率。

转到epoll()/ kqueue(),类似规格的计算机将轻松处理10,000个客户端,其中一些(功能更强大的计算机,但按今天的标准仍被视为很小的计算机)已经拥有30,000个客户端而没有中断汗。

我在SIGIO上进行的实验似乎表明,它对于延迟非常重要的应用程序非常有效,在这些应用程序中,只有很少的活动客户端仅进行很少的个人工作。

我建议在几乎任何情况下,都应在select()/ poll()上使用epoll()/ kqueue()。我没有尝试过在线程之间拆分客户端。老实说,我从未找到需要在前端客户端处理上进行更多优化工作以证明使用线程进行实验的服务。