为什么我会选择采用基于线程/进程的方法而不是异步Web服务器?

11

随着我对网络服务器软件的进一步研究,我开始质疑Apache的线程/进程模式是否比像Nginx和Lighttpd这样提供异步请求处理的服务器更好,在更重的负载下往往可以更好地扩展。

我知道后者与Apache之间还有许多其他差异。我的问题是,在什么情况下我会选择线程/进程模式而不是异步处理。

  1. 在使用异步方法时,有哪些功能/技术无法使用(或者无法正常工作/效果不佳)?

  2. 什么情况会导致异步方法的性能比线程/进程方法更差?这些情况是普遍的还是罕见的,差距有多大?

  3. 在比较两种方法时,还有其他因素应该考虑吗?请记住,我主要关注线程/进程方法与异步方法的比较,而不是任何特定的服务器软件,这些服务器软件可能会利用其中一种方法。这些问题可能涉及管理/调试的难度、安全问题等。


关于(3):开发人员的生产力。 - usr
我不太确定您所说的开发者生产力指的是什么。您能详细解释一下吗? - helloworld922
我认为usr的意思是,相对于处理多个异步请求的程序,使用线程服务器编程和阅读起来更加容易。然而,如果你每次只在池线程中处理一个请求(如果你确定每个请求都可以立即服务),那么这也可能同样容易。 - migle
实际上,异步处理可以支持更多的并发请求,因为异步操作比线程轻量级得多。异步操作不意味着为每个请求创建一个新线程或自己管理线程池。 然而,异步模式有些难以正确实现,并可能导致代码稍微难以阅读。 - Vladik Branevich
2个回答

5
这篇文章虽然有些老,但回答还是很有价值的。首先我们来说一下每种模型的工作方式。
在线程模型中,请求进入处理程序后,处理程序会生成一个新的操作系统线程来处理该请求,并且所有与该请求相关的工作都在该线程中进行,直到响应被发送并结束该线程。此模型支持与服务器可以生成的线程数量相同的并发请求数量(但线程可能比较重)。
在异步模型中,请求也是进入处理程序,但不会创建线程来处理它,而是将连接添加到所谓的事件循环中。事件循环监听连接上的数据/状态变化,并在每次“某事”发生时触发回调。一旦连接被添加到事件循环中,处理程序立即开始监听新连接以添加。这允许您同时拥有许多(有时为100K)并发连接。

是否有任何功能/技术无法在异步方法中使用(或者表现得不好/不如预期)?

当你进行数字计算时,异步(或“事件驱动”)系统的架构非常适合传递数据,但不适合处理数据。它可以处理数千个并发操作,但由于它仅在一个操作系统线程上运行,所以触发的回调需要尽可能少地执行操作以获得最大吞吐量。这是因为如果你的其中一个回调执行了需要5秒钟的数字计算,整个服务器将被冻结5秒钟,直到该操作完成。思路是获取数据,将其发送到目标位置(数据库、API等),并通过最小的处理来发送响应。
异步对于网络I/O非常好:在多个源/目标之间传递数据(还包括用户界面,但超出了本篇文章的范围)。
“什么情况会导致异步方法的性能比基于线程/进程的方法更差?这些情况是常见还是罕见,差异有多大?”
参考上文,但任何时候,如果你的 CPU 工作比网络 I/O 更多,你应该切换到线程模型。然而,有一些架构的变通方法……例如,你可以拥有一个异步应用程序,并且每次需要进行实际工作时,都将任务发送到工作者队列。然而,如果每个请求都需要 CPU 处理,那么这种架构就过度了,你最好使用线程化服务器。

在比较两者时,我还应该考虑其他因素吗?请记住,我主要关注基于线程/进程和异步的方法,而不是任何特定的服务器软件使用的方法。这些问题可能会包括管理/调试的难度、安全问题等。

与线程相比,异步编程 通常 更加复杂。尽管如此,如果你不是自己进行编程(即你在nginx和apache之间进行选择),那么我通常建议你选择异步(nginx),因为这样你通常能够从服务器中挤出更多的性能。我总是支持在堆栈中使用尽可能多的异步操作。
如果您正在编写一个应用程序并尝试决定是否使用线程或异步模型,那么您需要考虑开发人员的时间。除非您使用的是具有事件循环的绿色线程语言(如scheme),否则请期望会因为异常错误导致整个应用程序崩溃而感到非常困扰,并且通常需要掌握CPS/为所有内容使用回调函数。 Futures/promises是您的好朋友,但只是一种治标不治本的方法,使异步变得更加美好。

简而言之

如果您在服务器上使用异步,可以比线程模型实现更多并发操作,如果您只是进行网络IO而没有其他操作。

如果您要进行任何类型的数字计算,请使用线程应用程序服务器或者使用带有后台排队系统的异步应用。

除非您的语言支持其上的“假”线程(即绿色线程),否则异步编程要困难得多。一旦您克服了最初的障碍,通常情况下就没问题了。如果没有绿色线程,请使用promises。

如果在你的技术栈中(apache vs nginx)可以选择线程和异步组件,并且它们提供完全相同的功能,稍微偏向于异步。但不要仅仅因为你认为它会使一切变得快20倍而选择它。

0

进程相较于线程和异步模型在安全性和可靠性方面有许多优势。大部分的网站不需要这些特定的优势,但有时它们是必不可少的。

  1. 安全性:您可以在沙箱中运行工作进程,以低权限用户身份处理每个请求。这减轻了某些安全漏洞:即使攻击者完全接管了您的整个工作进程,只要您基于请求元数据严格地对其进行了沙箱隔离(即它无法写入所有数据),则它不能破坏系统稳定性或影响对请求所做出的响应。
  2. 安全性 #2:有时需要隔离不受信任的代码,或者强制实现不同代码或不同请求之间的隔离,唯一的方法就是使用单独的一次性进程(比如运行用户提供的代码)。
  3. 可靠性:如果定期拆除并替换工作进程(或针对每个请求进行拆除),则内存泄漏和内存损坏的严重性会大大降低。
  4. 很容易对在单独的进程中处理用户请求所花费的CPU时间、磁盘和网络配额等进行硬限制。即使请求处理代码进入了无限循环,主进程(或操作系统)也可以强制超时。

由于某些你无法控制的原因,你的进程可能会被阻塞在(例如)一个你没有明确启动的磁盘I/O上。如果你的工作分布在多个进程中,那么其他进程可以继续工作。如果是单个进程中的大量线程,则它们都会被冻结,直到该操作完成,从而显著影响吞吐量。 - Ron Burk

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