在后台使用 Netty 与 Tomcat 时 Spring webFlux 的差异

Posted

技术标签:

【中文标题】在后台使用 Netty 与 Tomcat 时 Spring webFlux 的差异【英文标题】:Spring webFlux differrences when Netty vs Tomcat is used under the hood 【发布时间】:2019-11-09 15:59:09 【问题描述】:

我正在学习spring webflux,我已经阅读了以下系列文章(first,second,third)

在第三篇文章中,我遇到了以下文字:

请记住,相同的应用程序代码在 Tomcat、Jetty 或 Netty 上运行。 目前,Tomcat 和 Jetty 支持是在 Servlet 之上提供的 3.1 异步处理,所以限制为每个线程一个请求。当相同的代码在 Netty 服务器平台上运行时, 约束解除,服务器可以分派请求 同情网络客​​户端。只要客户不 块,每个人都很高兴。 netty 服务器的性能指标和 客户端可能表现出类似的特征,但 Netty 服务器是 不限于每个线程处理一个请求,所以它 不使用大型线程池,我们可能希望看到一些 资源利用的差异。我们稍后再谈 在本系列的另一篇文章中。

首先我看不到该系列中较新的文章,虽然它是在 2016 年写的。我很清楚,tomcat 默认有 100 个线程用于处理请求,一个线程同时处理一个请求,但我不明白这句话每个线程只能请求一个是什么意思?

我还想知道 Netty 在具体案例中是如何工作的(我想了解与 Tomcat 的区别)。每个线程可以处理 2 个请求吗?

【问题讨论】:

我认为这是一个错字,它将 Servlet 3.0 异步和 Servlet 3.1 非阻塞 I/O 混为一谈。我会联系作者解决这个问题。 @Brian Clozel 但无论如何我有点搞砸了,我想请你提供一个正确的文本,只是因为我不明白短语 它仅限于每个线程一个请求 与 servlet 3.0 相关 从我目前的设想来看,它与 servlet 2.5 而不是 3+ 相关 文章正在修复中 @Brian Clozel 太棒了!!!我联系了合适的人)世界是如此之小 【参考方案1】:

目前有 2 个基本概念来处理对 Web 服务器的并行访问,各有优缺点:

    阻止 非阻塞

阻止网络服务器

阻塞、多线程服务器的第一个概念在池中具有有限数量的线程。每个请求都将被分配给特定线程,并且该线程将被分配,直到请求得到完全处理。这与超市结账排队的工作方式基本相同,一个顾客一次有可能的平行线。在大多数情况下,Web 服务器中的请求在处理请求时大部分时间都是 cpu-idle。这是因为它必须等待 I/O:读取套接字,写入 db(基本上也是 IO)并读取结果并写入套接字。此外,使用/创建一堆线程很慢(上下文切换)并且需要大量内存。因此,此概念通常不会非常有效地使用其拥有的硬件资源,并且对可以并行服务的客户端数量有硬性限制。此属性在所谓的饥饿攻击中被滥用,例如slow loris,这种攻击通常单个客户端可以毫不费力地 DOS 大型多线程 Web 服务器。

总结

(+) 更简单的代码 (-) 并行客户端的硬限制 (-) 需要更多内存 (-) 硬件在日常 Web 服务器工作中的使用效率低下 (-) 易于 DOS

大多数“传统”网络服务器都以这种方式工作,例如较旧的 tomcat、Apache Webserver 以及所有 Servlet 早于 3 或 3.1 等的东西。

非阻塞网络服务器

相比之下,非阻塞网络服务器可以只用一个线程为多个客户端提供服务。那是因为它使用了non-blocking kernel I/O features。这些只是内核调用,当可以写入或读取某些内容时,它们会立即返回并回调,从而使 cpu 可以腾出来做其他工作。再用我们超市的比喻,这就像,当收银员需要他的主管解决问题时,他不会等待并阻塞整条车道,而是开始结账下一位顾客,直到主管到达并解决第一个问题客户。

这通常在事件循环或更高抽象的green-threads 或fibers 中完成。从本质上讲,这样的服务器不能真正同时处理任何东西(当然你可以有多个非阻塞线程),但它们能够并行服务数千个客户端,因为内存消耗不会随着与多线程概念完全一样(阅读:最大并行客户端没有硬性限制)。也没有线程上下文切换。不利的一面是,非阻塞代码的读写通常更复杂(例如callback-hell),并且在请求执行大量 cpu 昂贵工作的情况下不会很好地执行。

总结

(-) 更复杂的代码 (-) CPU 密集型任务的性能更差 (+) 作为 Web 服务器更有效地使用资源 (+) 更多没有硬限制的并行客户端(最大内存除外)

大多数现代“快速”网络服务器和框架都支持非阻塞概念:Netty、Vert.x、Webflux、nginx、servlet 3.1+、Node、Go Webservers。

附带说明一下,看看这个基准页面,您会发现大多数最快的网络服务器通常都是非阻塞的:https://www.techempower.com/benchmarks/


另见

https://***.com/a/21155697/774398 https://www.quora.com/What-exactly-does-it-mean-for-a-web-server-to-be-blocking-versus-non-blocking

【讨论】:

您所写的是正确的,但我对弹簧网通量的问题。对于 tomcat spring web Flux 在 servlet 3.1(异步支持 + NIO)上工作,但对于 Netty,它的工作方式有所不同。我想知道怎么做。请关注link,找到我在话题中提到的那句话 记住相同的应用程序代码在 Tomcat、Jetty 或 Netty 上运行。目前,Tomcat 和 Jetty 支持是在 Servlet 3.1 异步处理之上提供的,因此它仅限于每个线程一个请求。我创建了这个主题,因为我不明白这句话 Tomcat 和 Jetty 是 servlet 容器,因此它们受限于 servlet 规范如何定义它们。 Servlet 3.1 应该启用非阻塞 IO,但我认为这不是强制性的。我认为这只是说他们不支持非阻塞概念的一种复杂方式? “对于 tomcat spring web Flux 在 servlet 3.1(异步支持 + NIO)上工作,但对于 Netty,它的工作方式有所不同。” Servlet 3.1 NIO 和 Netty 应该都使用相同的原理。 据我所知,tomcat 8.5+ 支持 servlet 3.1,因此它支持异步+非阻塞【参考方案2】:

使用 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。

【讨论】:

工作总是发生在服务器线程上,而且总是阻塞你是说容器线程吗? 是的,我的意思是一样的。 您是说自servlet 3.1 以来不再使用“一个请求/线程”模型。但是 servlet 3.0 呢?您是否将其视为“一个请求/线程”模型?我同意 servlet 3.1 中的 NIO 比 3.0 为单个请求分配的线程更多,但 3.0 也不是每个请求的单个线程 谢谢,现在很清楚了。另外,如果您分享任何使用 servlet 3.1(没有 webflux)的 spring“hello world”示例,我将不胜感激 我手头没有任何示例,但是这个演示文稿youtube.com/watch?v=uGXsnB2S_vc 和我们的反应式适配器github.com/spring-projects/spring-framework/blob/master/… 应该让您了解所涉及的复杂性。

以上是关于在后台使用 Netty 与 Tomcat 时 Spring webFlux 的差异的主要内容,如果未能解决你的问题,请参考以下文章

使用Netty作为业务服务器与Tomcat相比有何优势

使用Webflux和Spring Cloud时如何用netty替换tomcat?

tomcat nio 为啥不比netty

Tomcat从弱口令到后台上传大马

netty和tomcat如何一起使用??

问题精选-Netty+Tomcat