我们都阅读了基准测试并知道事实——基于事件的异步网络服务器比线程服务器更快。想想 lighttpd 或 Zeus 与 Apache 或 IIS。这是为什么?
4 回答
我认为基于事件与基于线程不是问题 - 它是非阻塞多路复用 I/O、可选套接字、解决方案与线程池解决方案。
在第一种情况下,无论使用什么输入,您都在处理所有输入 - 因此读取没有阻塞 - 单个“侦听器”。单个侦听器线程将数据传递给不同类型的工作线程,而不是每个连接一个。同样,不会阻塞写入任何数据——因此数据处理程序可以单独运行。因为这个解决方案主要是 IO 读取/写入,所以它不会占用太多 CPU 时间——因此您的应用程序可以用它来做任何它想做的事情。
在线程池解决方案中,您有单独的线程处理每个连接,因此它们必须共享时间来上下文切换进出 - 每个“侦听”。在这个解决方案中,CPU + IO 操作在同一个线程中——这会得到一个时间片——所以你最终会等待 IO 操作来完成每个线程(阻塞),这通常可以在不使用 CPU 时间的情况下完成。
Google for non-blocking IO for more details - 你也可以找到一些与线程池的比较。
(如果有人可以澄清这些观点,请随意)
事件驱动的应用程序并非天生就更快。
We examine the claimed strengths of events over threads and show that the
weaknesses of threads are artifacts of specific threading implementations
and not inherent to the threading paradigm. As evidence, we present a
user-level thread package that scales to 100,000 threads and achieves
excellent performance in a web server.
那是在 2003 年。从那时起,现代操作系统上的线程状态肯定有所改善。
编写基于事件的服务器的核心意味着在您的代码中重新发明协作式多任务处理(Windows 3.1 风格),很可能是在已经支持适当的抢先式多任务处理的操作系统上,并且没有透明上下文切换的好处。这意味着您必须管理通常由指令指针暗示或存储在堆栈变量中的堆上的状态。(如果你的语言有它们,闭包可以显着减轻这种痛苦。尝试在 C 中做到这一点就没那么有趣了。)
这也意味着您获得了协作式多任务处理所暗示的所有警告。如果您的某个事件处理程序由于某种原因需要一段时间才能运行,它会停止该事件线程。完全不相关的请求滞后。即使是冗长的 CPU 发明操作也必须发送到其他地方以避免这种情况。当您谈论高并发服务器的核心时,“冗长操作”是一个相对术语,对于预计每秒处理 100,000 个请求的服务器而言,大约为微秒。我希望虚拟内存系统永远不必为您从磁盘中提取页面!
从基于事件的架构中获得良好的性能可能会很棘手,尤其是当您考虑延迟而不仅仅是吞吐量时。(当然,线程也有很多错误。并发仍然很难。)
对于新服务器应用程序的作者,有几个重要问题:
- 线程在您今天打算支持的平台上如何执行?他们会成为你的瓶颈吗?
- 如果您仍然坚持使用错误的线程实现:为什么没有人解决这个问题?
这真的取决于你在做什么;对于非平凡的应用程序,基于事件的编程肯定是棘手的。作为 Web 服务器确实是一个非常容易理解的微不足道的问题,事件驱动和线程模型在现代操作系统上都运行得很好。
在事件模型中正确开发更复杂的服务器应用程序通常非常棘手 - 线程应用程序更容易编写。这可能是决定因素而不是性能。
这实际上与线程无关。这是关于线程用于服务请求的方式。对于像 lighttpd 这样的东西,你有一个通过事件为多个连接提供服务的线程。对于旧版本的 apache,每个连接都有一个进程,并且该进程在传入数据时唤醒,因此当有很多请求时,您最终会得到一个非常大的数字。但是,现在使用 MPM apache 是基于事件的,请参阅apache MPM event。