Spring WebFlux和Reactor的线程模型

56

目前正在使用 Spring 5.0.0.RC2Reactor 3.1.0.M2Spring Boot 2.0.0.M2 进行响应式编程实验。

想知道 WebFlux 和 Reactor 使用的并发和线程模型,以便正确编写应用程序并处理可变状态。

Reactor文档说明该库被认为是并发无关的,并提到了调度器抽象。WebFlux文档没有提供信息。

然而,当通过Spring Boot使用WebFlux时,会定义一种线程模型。

从我的实验中得出以下结论:

  • 该模型既不是一个事件线程,也不是一个事件线程+工作线程
  • 使用了多个线程池
  • "reactor-http-nio-3" 线程:可能是每个核心一个,处理传入的HTTP请求
  • "Thread-7" 线程:由异步请求到MongoDB或HTTP资源使用
  • "parallel-1" 线程:每个核心一个,由Reactor的Schedulers.parallel()创建,用于延迟操作等
  • 共享可变状态必须由应用程序同步
  • ThreadLocal(用于应用程序状态、MDC日志记录等)不是请求范围的,因此不是非常有趣

这是否正确?WebFlux的并发和线程模型是什么:例如,默认的线程池是什么?

感谢提供信息。


1
“WebFlux和Reactor的并发和线程模型”取决于您的应用程序代码。Spring WebFlux和Reactor都不会强制您使用任何并发模型。您需要研究并发响应式编程。 - Abhijit Sarkar
你实际上在询问Spring、Reactor、Netty、响应式数据库驱动等的“线程模型”。没有人能够正确回答这个问题。 - Brian Clozel
1
@BrianClozel,你能指出一些关于Spring WebFlux线程默认配置的文档吗?“事件循环”线程是否像Vertx一样默认按CPU核心数启动? - zennon
1
@BrianClozel 我也很困惑。在我们的应用程序中,我们看到了elastic-2、elastic-evictor-1、parallel-1、reactor-http-nio-1、reactor-http-nio-2、reactor-http-nio-3……一直到reactor-http-nio-40:这种类型的线程有40个。这个Spring Boot 2.0.0.RC1运行在Alpine Docker镜像上。同样的应用程序在我的4核笔记本电脑上有4个reactor-http-nio线程。这些都是什么意思,在文档中没有适当的提及,真是让人难过,看到Spring团队如此疏忽。 - ROCKY
2
Spring WebFlux有大量的文档资料可供参考,具体请查看https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux。与其说这些努力被忽视了,不如通过识别缺失之处来帮助改进它,例如请参阅https://jira.spring.io/browse/SPR-16538。 - Rossen Stoyanchev
2
@RossenStoyanchev,这仍然无法解释我们看到的错误。使用Alpine Linux发行版并使用Spring Webflux,反应器线程将达到40个线程。如果使用其他Linux发行版(如CentOS)或Windows笔记本电脑使用相同的应用程序,则使用默认值为2 *可用内核。 - ROCKY
1个回答

53
在提问之后,现有文档确实提供了有关并发模型和线程的一些线索,可以预期会出现哪些线程(但我仍然认为,从多线程角度更清晰/更好的描述在幕后发生的事情将受到Spring新手的高度赞赏)。
它讨论了Spring MVC和Spring WebFlux之间的区别(1个请求模型对应一个线程与事件循环):
在Spring MVC和通常的servlet应用程序中,假定应用程序可能阻塞当前线程,例如远程调用,因此servlet容器使用大型线程池来吸收请求处理期间的潜在阻塞。
在Spring WebFlux和非阻塞服务器中,假定应用程序不会阻塞,因此非阻塞服务器使用小型固定大小的线程池(事件循环工作线程)来处理请求。调用阻塞API
但请注意,Spring MVC应用程序也可以引入一些异步性(参见Servlet 3 Async)。我建议这个演示文稿讨论Servlet 3.1 NIO和WebFlux。
回到文档:它还建议,在使用反应流时,您具有一定的控制权:
如果确实需要使用阻塞库怎么办?
Reactor和RxJava都提供了publishOn运算符,以在不同的线程上继续处理。

关于此更多细节,请参考Reactor中的调度

它还讨论了您可能在WebFlux应用程序中遇到的线程(加粗是我的注释):

线程模型

在运行Spring WebFlux的服务器上,您应该期望看到哪些线程?

  • 在“纯净”的Spring WebFlux服务器上(例如没有数据访问或其他可选依赖项),您可以期望看到一个用于服务器和多个用于请求处理的线程(通常与CPU内核数量相同)。但是,Servlet容器可能会启动更多线程(例如,在Tomcat上启动10个线程),以支持servlet、阻塞I/O和servlet 3.1非阻塞I/O使用。
  • 反应式WebClient采用事件循环风格。因此,您将看到与之相关的少量固定数量的处理线程,例如使用Reactor Netty连接器的“reactor-http-nio-”。但是,如果Reactor Netty同时用于客户端和服务器,则两者默认共享事件循环资源。
  • Reactor和RxJava提供了线程池抽象,称为Schedulers,可与publishOn操作符一起使用,用于切换处理到不同的线程池。这些调度程序具有暗示特定并发策略的名称,例如“parallel”用于CPU绑定工作,具有有限数量的线程,或“elastic”用于I/O绑定工作,具有大量线程。如果您看到这些线程,这意味着某些代码正在使用特定的线程池调度程序策略。
  • 数据访问库和其他第三方依赖项也可能创建并使用自己的线程

一部分可以通过配置来配置线程模型的详细信息。

要为服务器配置线程模型,您需要使用特定于服务器的配置API,或者如果使用Spring Boot,则检查每个服务器的Spring Boot配置选项。WebClient可以直接配置。对于所有其他库,请参考其各自的文档。

此外,如讨论Spring boot 2.0响应式webflux配置中默认线程数所述,

请求处理的默认线程数由底层Web服务器确定;默认情况下,Spring Boot 2.0使用Reactor Netty,该服务器使用Netty的默认值。

这是默认组件及其默认值(以及透明地通过注释注入的整体配置)的问题 -- 这也可能会随着Spring/Boot及其相应依赖关系的版本而发生变化。话说回来,您的猜测似乎是正确的。


4
如果我的反应式端点需要调用一个执行阻塞操作的外部非反应式端点,那么我的反应式端点是否仍然是反应式的?我使用反应式WebClient来调用这个外部非反应式端点。 - user1955934
如果您正在使用由第三方提供的SDK,则还需要是响应式的。 如果您只是调用另一个端点,则无关紧要,因为您自己的计算机正在释放线程,使其不被任何响应阻塞。 - Federico

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