2 回答
TA贡献1784条经验 获得超8个赞
目前有 2 个基本概念来处理对具有各种优点和缺点的 Web 服务器的并行访问:
阻塞
非阻塞
阻止网络服务器
阻塞、多线程的第一个概念服务器在池中有有限数量的线程。每个请求都将分配给特定的线程,并且该线程将被分配直到请求被完全服务。这与超市结账队列的工作方式基本相同,一次一个顾客,可能有平行线。在大多数情况下,Web 服务器中的请求在处理请求的大部分时间里都是 cpu 空闲的。这是因为它必须等待 I/O:读取套接字、写入数据库(基本上也是 IO)并读取结果并写入套接字。此外,使用/创建一堆线程很慢(上下文切换)并且需要大量内存。因此,这个概念通常不会非常有效地使用它拥有的硬件资源,并且对可以并行服务的客户端数量有硬性限制。slow loris,一种通常单个客户端可以毫不费力地 DOS 大型多线程 Web 服务器的攻击。
概括
(+) 更简单的代码
(-) 并行客户端的硬限制
(-) 需要更多内存
(-) 通常的网络服务器工作硬件使用效率低下
(-) 易于 DOS
大多数“传统”网络服务器都是这样工作的,例如旧的 tomcat、Apache 网络服务器以及所有Servlet
早于 3 或 3.1 的东西等等。
非阻塞网络服务器
相比之下,非阻塞网络服务器可以只用一个线程为多个客户端提供服务。那是因为它使用了非阻塞内核 I/O 特性。这些只是内核调用,当可以写入或读取某些内容时会立即返回并回调,从而使 cpu 可以自由地做其他工作。重用我们的超市比喻,这就像,当收银员需要他的主管解决问题时,他不会等待并阻塞整个通道,而是开始结账下一位顾客,直到主管到达并解决第一位顾客的问题顾客。
这通常在事件循环或更高的抽象中完成,如green-threads或fibers。本质上这样的服务器不能真正并发处理任何事情(当然你可以有多个非阻塞线程),但它们能够并行服务数千个客户端,因为内存消耗不会像多线程那样急剧扩展概念(阅读:最大并行客户端没有硬性限制)。也没有线程上下文切换。缺点是,非阻塞代码通常更难以读写(例如callback-hell),并且在请求执行大量 cpu 昂贵工作的情况下效果不佳。
概括
(-) 更复杂的代码
(-) cpu 密集型任务的性能更差
(+) 作为 Web 服务器更有效地使用资源
(+) 更多没有硬限制的并行客户端(最大内存除外)
大多数现代“快速”网络服务器和框架促进了非阻塞概念:Netty、Vert.x、Webflux、nginx、servlet 3.1+、Node、Go 网络服务器。
作为旁注,查看此基准页面,您会发现大多数最快的 Web 服务器通常都是非阻塞服务器: https: //www.techempower.com/benchmarks/
TA贡献1884条经验 获得超4个赞
使用 Servlet 2.5 时,Servlet 容器会将请求分配给线程,直到该请求已被完全处理。
使用 Servlet 3.0 异步处理时,服务器可以在应用程序处理请求时在单独的线程池中分派请求处理。然而,当涉及到 I/O 时,工作总是发生在服务器线程上,并且它总是阻塞的。这意味着“慢客户端”可以独占服务器线程,因为服务器在读取/写入网络连接较差的客户端时被阻塞。
使用 Servlet 3.1,允许异步 I/O,在这种情况下,“一个请求/线程”模型不再存在。在任何时候,位请求处理都可以安排在服务器管理的不同线程上。
Servlet 3.1+ 容器通过 Servlet API 提供了所有这些可能性。利用异步处理或非阻塞 I/O 取决于应用程序。在非阻塞 I/O 的情况下,范式的改变很重要,而且使用起来真的很有挑战性。
使用 Spring WebFlux - Tomcat、Jetty 和 Netty 没有完全相同的运行时模型,但它们都支持反应式背压和非阻塞 I/O。
添加回答
举报