为什么Apache事件MPM表现不佳?

24

Event MPM设计并不与Nginx完全相同,但显然是为了使长连接更可行和发送静态文件更快而设计的。我的理解是,事件MPM有些名称不当,因为:

  1. 虽然连接已传递到kqueue / epoll,
  2. 某些非常重要的模块,如mod_gzip和mod_ssl将阻塞/消耗线程,直到响应完成,
  3. 对于大文件来说是个问题,但对于PHP生成的HTML文档等可能没有问题。

不幸的是,Apache一直在失去市场份额,而大多数基准测试都对事件MPM进行了谴责。 基准测试是否有缺陷,还是事件MPM确实比Nginx表现差? 即使存在这些限制,在正常流量(非恶意)和较小文件下,它应该在某种程度上与Nginx竞争。 例如,与Nginx相比,在缓慢的连接上通过php-fpm提供服务的PHP生成文档应该具有一定的竞争力,因为文档将被缓冲(即使使用ssl和gzip也是如此)并异步发送。 在此工作负载下,使用压缩或不使用压缩的SSL和非SSL连接不应与Nginx有任何实质性不同。

那么为什么它在各种基准测试中表现不佳? 它有什么问题? 还是基准测试有问题? 是否有一个主要的网站在使用它作为权威机构的吸引力,即它可以发挥性能?


4
如果您引用了基准测试结果,这将是一个更好的问题。如果您只是想要快速安装,则预分叉的Apache + mod_php在低到中等负载下始终比Nginx性能更好 - 在高负载下有巨大的差异。但是,如果您想要容量和性能,那么您应该考虑不同的架构 - 将ATS/nginx/varnish放在您的Web服务器前面。 - symcbean
@symcbean 可能是真的。我只是还没有找到一个看起来不错的(除了Apache发布的那个)。我也认为你过于概括了。ATS或Varnish在无法缓存的动态内容上几乎没有作用(或者会有害)。 - Jaimie Sirovich
相反,将基于事件的服务器运行在预分叉的Web服务器前面将提供保护,防止Sloloris攻击,并卸载静态内容服务,节省内存并增加容量。动态内容的额外延迟应该是几毫秒的数量级 - 几乎没有什么影响。实际上,如果您可以将反向代理移动到更靠近客户端的位置,您应该会看到性能有显着的改善。 - symcbean
@symcbean 我只是在提到动态内容。请看“... on dynamic content that cannot be cached.” 它可能会对slowloris和mod_reqtimeout或mod_security上的慢速读取做出一些处理,但代价是通过管道复制另一个副本。我想你可以说Nginx + Apache+mod_php在技术上与Nginx + php-fpm并没有太大区别。但是为什么有人要使用php-fpm呢?难道php-fpm和FastCGI到本地主机比通过本地主机的HTTP更快吗?无论如何,正如另一个答案所述,事件MPM大致相当于您描述的Nginx - 一个基于事件的服务器。 - Jaimie Sirovich
2个回答

21
与nginx相比,使用事件MPM的Apache(非常)大致相当于在工作人员MPM前面使用事件驱动的HTTP代理(nginx、varnish、haproxy)。事件 is worker,但是与将每个新连接交给线程终身不同,事件MPM的线程将连接交给辅助线程,后者将其推入队列或关闭它(如果keep-alive关闭或已过期)。
事件相对于工作人员的真正好处是资源使用情况。如果您需要维持1,000个并发连接,则工作人员MPM需要1,000个线程,而事件MPM可能通过100个活动线程和900个在事件队列中管理的空闲连接。事件MPM将在假设中使用工作人员MPM的一小部分资源,但缺点仍然存在:每个请求都由单独的线程处理,必须由内核进行调度,并且因此会产生上下文切换的成本。
另一方面,我们有nginx,它本身使用事件模型作为其调度程序。 Nginx只需在移动到下一个连接之前尽可能多地处理每个连接上的工作即可。无需额外的上下文切换。
事件MPM真正闪耀的一个用例是处理在Apache中运行重型应用程序的设置,并保存在保持活动期间处于空闲状态的线程的资源,您将在Apache前部署代理(例如nginx)。如果您的前端没有其他用途(例如静态内容、代理到其他服务器等),则事件MPM将美丽地处理该用例,并消除了代理的需要。

这就是为什么如果你使用Apache事件MPM + PHP-fpm,我期望它表现良好的原因。 它不应该比Nginx + PHP-fpm做得更多或更差。 但是每个基准测试都表明它并没有。 是时候进行自己的基准测试了。 顺便说一句,他们真的可以将这种“保持活动优化”应用于每个MPM,包括prefork。 没有任何东西会阻止他们使用主进程来跟踪空闲的保持连接,并在活动时仅将它们移交给池中的进程。 他们只是对worker这样做了。 - Jaimie Sirovich
1
我所做的比较是 Nginx + Apache-worker + PHP-fpm ~~ Apache-event + PHP-fpm。Nginx + PHP-fpm 总是比 Apache-event + PHP-fpm 表现显著更好,因为在 Apache 下每个 FastCGI 连接都必须由单独的线程处理。如果 php 是线程安全的,你可以通过使用 Apache-event+mod_php 来实现更接近 Nginx 的性能,但不幸的是,它并不是... - Ben Grimm
也许我误解了,但线程的生命周期将非常短,因为大多数连接到php-fpm的连接都很短暂。响应将很小(HTML文档通常为10-100k),会被缓冲并以事件驱动的方式发送。我想Nginx可以完全不需要每个连接一个线程,而是使用状态机与php-fpm通信。它可能也会进行缓冲,否则它将重新创建prefork在慢速连接上遇到的问题,并将其推回到php-fpm进程而不是httpd进程。我认为差异不会太大。 - Jaimie Sirovich
据我所知,如果响应通过事件队列进行缓冲和传递,像mod_gzip这样的模块就不会在响应期间占用线程。运行事件队列的线程存在于HTTP请求之间的连接中。因此,在您的情况下,当下一个请求进来时,事件循环将交给一个线程来处理它,该线程发送一个请求到php-fcgi,php-fcgi处理并返回,apache线程发送响应并将连接交给事件循环。Nginx可以跳过交接并在其事件循环中与fcgi通信。 - Ben Grimm
尽管如此,Nginx仍然有一些CPU绑定的任务要完成,例如SSL和压缩。这就是为什么通常将其配置为NUM_CPUs + 1或更多进程的原因,而不是像轻量级的Lighttpd那样只有一个进程。我猜Event可能会有所改善,但如果有人添加.htaccess支持并使其与CPanel兼容,那么Apache看起来真的会凋零。 - Jaimie Sirovich
显示剩余2条评论

9
对我来说,主要的操作区别在于:
  • 处理程序(负责生成响应的插件)是同步的——如果它们执行计算或I/O操作,它们将占用一个线程
  • 核心必须使用跨线程锁来保护关键数据结构,因为它是多线程的,以支持如此多的同步请求
这就是为什么在非常高的工作负载下,像nginx(或Apache Traffic Server或任何现代商业/高性能代理)通常更胜一筹。
我认为你问题中的重点不太准确,SSL和deflate对这里的差异没有真正的贡献,因为它们都是过滤器,不会对可扩展性问题产生影响,甚至不会将httpd绑定到其传统API对请求或连接的生命周期的保证。这些过滤器(与处理程序或负责低级I/O的核心过滤器相比)可能是与处理模型相关性最小的事情之一。
但我也不认为它在所有但最极端的工作负载或极度受限制的系统上表现得那么糟糕。我看到的大多数基准测试都质量极差,出于某种原因。
我认为大多数人想要他们所谓的Web服务器成为更复杂的应用服务器(Java EE、PHP等)的代理,而设计用于最有效地移动I/O的服务器而没有API负担的优势将会占据上风。

我应该补充说明,作为Apache httpd的贡献者,我可能有偏见。 - covener
1
谢谢!我承认是从代码和文档的快速审核中猜测的。我的处理程序是正确的,但具体细节是错误的,而且我错过了锁定。我提出这些模块的原因是,我可以看到它在大型静态文件上可能会成为一个大问题。Google希望我们在2015年转移到全SSL。我甚至可以看到SSL移动到内核,因为我们不能再只映射文件并发送了。但至少在Nginx中,它不会一直占用我们的线程。它可以使用ssh/gzip+epoll在几个线程上逐块阻塞,也许还有一些亲和力。我错了吗?gzip引发了类似的问题。 - Jaimie Sirovich

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