我有一个用C编写的守护进程,它需要同时处理20-150K的TCP连接。它们是长期连接,很少被拆除。在任何给定时间,它们在传输中都有非常少量的数据(很少超过MTU,它是刺激/响应协议),但对它们的响应时间至关重要。我想知道当前的UNIX社区正在使用什么来获取大量套接字,并最大限度地减少响应的延迟。我已经看到围绕多路复用的设计连接到fork工作池,线程(每个连接),静态大小的线程池。有什么建议吗?
答案 0 :(得分:7)
最简单的建议是使用libevent,这样可以轻松编写符合您要求的简单非阻塞单线程服务器。
如果每个响应的处理需要一些时间,或者如果它使用一些阻塞API(几乎像数据库中的任何东西),那么你需要一些线程。
一个答案是工作线程,在那里你产生一组线程,每个线程都在一些队列上监听工作。如果你愿意,它可以是单独的进程,而不是线程。主要区别在于告诉工人要做什么的沟通机制。
另一种方法是使用多个线程,并为每个线程提供150K连接的一部分。每个都有它自己的进程循环,并且主要像单线程服务器一样工作,除了监听端口,它将由单个线程处理。这有助于在核心之间分配负载,但如果使用阻塞资源,它将阻止此特定线程处理的所有连接。
libevent让你使用第二种方式;但也有另一种选择:libev。它并不像libevent那样众所周知,但它特别支持多循环方案。
答案 1 :(得分:2)
如果性能至关重要,那么您真的想要一个多线程事件循环解决方案 - 即一个工作线程池来处理您的连接。不幸的是,没有抽象库可以在大多数Unix平台上运行(注意libevent只是单线程,因为大多数这些事件循环库),所以你必须自己做脏工作。
在Linux上,这意味着将边缘触发的epoll 与工作线程池一起使用(Windows将具有在多线程环境中也能正常工作的I / O完成端口 - 我不确定其他Unix )。
顺便说一句,我已经做了一些工作,尝试在 http://nginetd.cmeerw.org上的Linux和Windows I / O完成端口上抽象边缘触发的epoll(它正在进行中,但可能会提供一些想法)
答案 2 :(得分:1)
如果你有系统配置访问不要过度并在n个守护进程实例(进程)中设置一些iptables / pf / etc到负载平衡连接 )因为这将开箱即用。取决于守护程序n的性质应该如何阻塞系统上的核心数量或者高几倍。这种方法看起来粗糙,但它可以处理损坏的守护进程,甚至可以在必要时重启它们。此外,迁移将是顺利的,因为您可以开始将新连接转移到另一组进程(例如,新版本或迁移到新框)而不是服务中断。最重要的是,您可以获得一些功能,例如源关联性,这可以帮助显着解决有问题的会话缓存和争用。
如果您没有系统访问权限(或者操作不能被打扰),您可以使用负载均衡器守护程序(有大量开源代码)而不是iptables / pf / etc并使用n个服务守护进程,如上所述。
此方法也有助于分离端口的权限。如果外部服务需要在低端口(<1024)上进行服务,则只需要负载均衡器运行特权/或admin / root或内核。)
我以前写了几个IP负载均衡器,在生产中可能非常容易出错。您不想支持和调试它。此外,与外部代码相比,操作和管理更倾向于猜测代码。
答案 3 :(得分:1)
我认为哈维尔的答案最有意义。如果你想测试理论,那么看看node javascript项目。
Node基于Google的v8引擎,该引擎将javascript编译为机器代码,并且对于某些任务来说与c一样快。它也基于libev,设计为完全无阻塞,这意味着您不必担心线程之间的上下文切换(一切都在单个事件循环上运行)。在这方面它与erlang非常相似。
使用节点在javascript中编写高性能服务器现在非常非常简单。您还可以通过一点点努力,在c中编写自定义代码并为节点创建绑定以调用它来进行实际处理(查看节点源以了解如何执行此操作 - 文档有点粗略此时此刻)。作为一个更加丑陋的替代方案,您可以将自定义c代码构建为应用程序,并使用stdin / stdout与其进行通信。
我自己用超过150k的连接测试了节点,绝对没有问题(当然,如果所有这些连接都要一次通信,你将需要一些严肃的硬件)。 node.js中的TCP连接平均只使用2-3k内存,因此理论上每1GB RAM可以处理350-500k连接。
注意 - Windows目前不支持Node.js,但它只处于开发的早期阶段,我想它会在某个阶段被移植。
注2 - 您必须确保从Node调用的代码不会阻止
答案 4 :(得分:0)
已经开发了几个系统来改进select(2)性能:kqueue,epoll和/dev/poll
。在所有这些系统中,您可以拥有一个等待任务的工作线程池;当完成其中一个文件时,你不会被迫一遍又一遍地设置所有文件句柄。
答案 5 :(得分:0)