我目前正在尝试构建一个http服务器。服务器由一个监听线程使用select(...)和由线程池管理的四个工作线程进行多线程处理。我目前在Core I3 330M上每秒管理大约14k-16k的请求,文件长度为70字节,响应时间为6-10ms。但这是没有保持活力的,我服务的任何插座在工作完成时我立即接近。
编辑:当检测到套接字上的活动时,工作线程处理已调度的“作业”,即。服务请求。 “工作”完成后,如果没有“工作”,我们会睡觉,直到有更多的“工作”被派遣,或者如果已经有一些“工作”,我们就会开始处理其中的一个。
当我开始尝试实现keep-alive支持时,我的问题就出现了。通过keep-alive激活,我只需要每秒管理1.5k-2.2k请求,并且有100个开放套接字。这个数字增加到大约12k,1000个开放插座。在这两种情况下,响应时间大约为60-90ms。我觉得这很奇怪,因为我目前的假设认为请求应该上升,而不是下降,响应时间应该会下降,但绝对不会上升。
我尝试了几种不同的策略来解决低绩效问题:
保持活动套接字存储在一个简单的链接列表中,其添加/删除方法由pthread_mutex锁定,负责重建FD_SET的函数也具有此锁定。
我怀疑这是互斥锁的持续锁定/解锁是这里的主要罪魁祸首,我试图描述问题,但gprof或google-perftools都不是非常合作,要么引入极端不稳定,要么明显拒绝收集所有数据(这可能是我不知道如何正确使用这些工具。)。但是删除锁可能会使链表处于不合理的状态,并可能导致程序崩溃或将程序置于无限循环中。 当我使用它时,我也怀疑select(...)/ pselect(...)超时,但我非常有信心这不是问题,因为即使没有它也会保持低性能。 / p>
我不知道应该如何处理保持活动的套接字,我想知道你们那里的人是否对如何修复低性能有任何建议,或者对我可以使用的任何替代方法有任何建议去支持keep-alive套接字。
答案 0 :(得分:6)
尝试彻底摆脱选择。您可以在每个流行的平台上找到某种事件通知:freebsd()上的kqueue / kevent,Linux上的epoll等。这样您就不需要重建FD_SET并且可以随时添加/删除监视的fds。
答案 1 :(得分:0)
有很多选择:
accept()
直接在工作线程上。答案 2 :(得分:0)
您的测试客户端是否重用套接字?他们正确处理还活着吗? 我可以看到这样一种情况:您只需传递keep alive标头即可在基准测试代码中进行最小的更改,但不会更改您的代码,以便在收到付费数据包后在客户端关闭套接字。 这将带来所有保持活力的成本,而没有任何好处。
答案 3 :(得分:0)
您之前尝试做的事情已经完成。考虑阅读Leader-Follower网络服务器模式http://www.kircher-schwanninger.de/michael/publications/lf.pdf
答案 4 :(得分:0)
当客户端将套接字用于多个请求时,时间增加将更加明显。如果您只是打开和关闭但仍然告诉客户端保持活着,那么您的情况与没有保持活动状态的情况相同。但是现在你有插座的开销。
但是,如果您从同一客户端多次使用套接字进行多次请求,那么您将失去TCP连接开销并以此方式获得性能。
确保您的客户正确使用keepalive。并且可能是获得套接字状态和数据通知的更好方法。也许是民意调查设备或排队请求。
http://www.techrepublic.com/article/using-the-select-and-poll-methods/1044098
此页面有一个用于处理民意调查设备的Linux补丁。也许对它是如何工作有一些了解,你可以在你的应用程序中使用相同的技术,而不是依赖于可能没有安装的设备。