23

TornadowebNginx是目前流行的 Web 服务器,许多基准测试表明它们在某些情况下比 Apache 具有更好的性能。所以我的问题是:

'epoll' 是让它们如此之快的最根本原因吗?如果我想编写一个好的套接字服务器,我能从中学到什么?

4

2 回答 2

67

如果您正在寻找编写套接字服务器,一个很好的起点是几年前 Dan Kegel 的 C10k 文章:

http://www.kegel.com/c10k.html

我还发现 Beej 的网络编程指南非常方便:

http://beej.us/guide/bgnet/

最后,如果您需要很好的参考资料,可以参考 W. Richard Stevens 等人的 UNIX Network Programming。人:

http://www.amazon.com/Unix-Network-Programming-Sockets-Networking/dp/0131411551/ref=dp_ob_title_bk

无论如何,要回答您的问题,Apache 和 Nginx 之间的主要区别在于 Apache 每个客户端使用一个线程并具有阻塞 I/O,而 Nginx 是单线程且具有非阻塞 I/O。Apache 的工作池确实减少了启动和销毁进程的开销,但是在为多个客户端提供服务时,它仍然使 CPU 在多个线程之间切换。另一方面,Nginx 在一个线程中处理所有请求。当一个请求需要发出网络请求(例如,到后端)时,Nginx 将回调附加到后端请求,然后处理另一个活动的客户端请求。实际上,这意味着它返回到事件循环 ( epoll, kqueue, 或select) 并要求提供需要报告的文件描述符。请注意,主事件循环中的系统调用实际上是一个阻塞操作,因为在其中一个文件描述符准备好读取或写入之前,没有什么可做的。

所以这就是 Nginx 和 Tornado 能够高效地同时为许多客户端服务的主要原因:只有一个进程(从而节省 RAM)和一个线程(从而从上下文切换中节省 CPU)。至于epoll,它只是select的一个更高效的版本。如果有 N 个打开的文件描述符(套接字),它可以让您在 O(1) 而不是 O(N) 时间内挑选出准备读取的文件描述符。事实上,Nginx 可以使用 select 来代替 epoll,如果你用--with-select_moduleoption 编译它,我敢打赌它仍然会比 Apache 更高效。我对 Apache 内部结构不太熟悉,但快速 grep 显示它确实使用 select 和 epoll —— 可能是当服务器正在侦听多个端口/接口时,或者如果它同时对单个客户端进行后端请求。

顺便说一句,我开始尝试编写一个基本的套接字服务器,并想弄清楚 Nginx 是如何如此高效的。在仔细研究了 Nginx 源代码并阅读了我上面链接的那些指南/书籍之后,我发现编写 Nginx 模块而不是我自己的服务器会更容易。因此诞生了现在半传奇的 Emiller Nginx 模块开发指南:

http://www.evanmiller.org/nginx-modules-guide.html

(警告:本指南是针对 Nginx 0.5-0.6 编写的,API 可能已更改。)如果您使用 HTTP 做任何事情,我会说给 Nginx 一个机会,因为它已经解决了与愚蠢客户端打交道的所有毛茸茸的细节。例如,我为好玩而编写的小型套接字服务器在所有客户端上都能很好地工作——除了 Safari,我从来不知道为什么。即使对于其他协议,Nginx 也可能是正确的选择;事件从协议中很好地抽象出来,这就是它可以代理 HTTP 和 IMAP 的原因。Nginx 代码库组织得非常好,编写得非常好,但有一个例外值得一提。在手动滚动协议解析器时,我不会效仿它。相反,使用解析器生成器。我在这里写了一些关于在 Nginx 中使用解析器生成器(Ragel)的东西:

http://www.evanmiller.org/nginx-modules-guide-advanced.html#parsing

所有这些信息可能比您想要的更多,但希望您会发现其中的一些有用。

于 2010-05-24T18:37:22.147 回答
5

是和不是。虽然他们都使用 epoll,但从技术上讲,他们都使用事件循环来处理请求。您可以在wikipedia上找到有关什么是事件循环以及如何使用它们的更多信息。

查看libevent(由gevent使用,通常比 tornado 更快、更稳定)或libev的实现。

于 2010-04-12T20:26:43.207 回答