弹簧卷筒和反应器的螺纹模型

目前正在试验Spring 5.0.0.RC2Reactor 3.1.0.M2Spring Boot 2.0.0.M2的反应式编程。

想知道WebFlux和Reacter使用的并发和线程模型来正确编码应用程序并处理可变状态。

Reactor 文档指出,该库被视为并发不可知论者,并提到了调度程序抽象。WebFlux 文档不提供信息。

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

从我的实验中,我得到了:

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

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

感谢您提供的信息


答案 1

在问题之后,本文档确实提供了一些关于并发模型和人们可以期望的线程的线索(但我仍然认为,从多线程的角度来看,更清晰/更好地描述场景下发生的事情会受到Spring新手的高度赞赏)。

它讨论了Spring MVC和Spring WebFlux之间的区别(1-thread-per-request模型与event-loop):

在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 运算符一起使用调度程序的名称暗示了特定的并发策略,例如,“并行”表示具有有限数量的线程的 CPU 密集型工作,或“弹性”表示具有大量线程的 I/O 绑定工作。如果您看到这样的线程,则意味着某些代码正在使用特定的线程池计划程序策略。
  • 数据访问库和其他第三方依赖项也可以创建和使用自己的线程

在某种程度上,您可以通过配置来配置线程模型的详细信息

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

此外,例如讨论Spring boot 2.0反应式webflux配置中默认线程数突出显示,

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

这是一个默认组件及其默认值(以及整体配置,包括通过注释透明注入的配置)的问题 - 它们也可能在Spring/Boot版本和相应的依赖项之间发生变化。这么说吧,你的猜测似乎是正确的。


答案 2