springboot webflux reactor并发模型
Springboot webflux reactor concurrency model
我想了解更多关于springboot webflux的底层并发模型?
对于CPU密集的网络服务,传统的阻塞多线程模型是否更适合?还是根据本文 https://people.eecs.berkeley.edu/~brewer/papers/threads-hotos-2003.pdf?
一般而言,传统线程池模型更适合吗?
如果我在反应器链中有一个阻塞步骤,我会使用 publishOn 将其安排到不同的线程池。这会释放原始线程并使整个链仍然是非阻塞的吗?
WebFlux 适合的地方及其背后的工作原理
从我的角度来看,最适合 Spring WebFlux 的是网络密集型应用程序。在幕后,Spring WebFlux 使用 Reactor-Netty,它是 Netty 的 包装器。 Reactor-Netty 使用几乎相同的 Netty 线程策略并为其内部 EventLoopThreadPool
创建 Runtime.getRuntime().availableProcessors() * 2
个线程。这意味着每个 Thread
都绑定到它自己的 core/processor 并且对 CPU 资源的争用最少。这种模型非常适合端到端的非阻塞通信。因此,在传入请求以远程网络调用结束的情况下,该调用应以非阻塞方式执行,以便同一线程可以返回到偶数循环队列中的其余任务。这种技术使我们能够有效地利用我们的硬件,几乎没有花费在上下文切换和高争用上的开销,这是与大量相关线程阻塞通信的常见 属性。
Project Reactor 的作用
Project Reactor 在 SpringWebFlux 中的核心作用是提供一种编程模型,以保持复杂的非阻塞、异步执行的清晰度。它隐藏了数据处理延续的复杂性,并允许我们轻松构建一个功能性的、声明性的元素处理管道。此外,Reactor 的线程模型只是几个运算符,可以在专用线程池上轻松进行复杂而高效的元素处理重新安排。在引擎盖下,使用从 Java 核心库中获取的相同 ThreadPools 和 ExecutorServices。
如何处理 CPU 密集型任务,Project Reactor 适合吗?
我会说 - Reactor Netty 也非常适合 CPU 密集型任务。但在那种情况下,应适当使用 Project Reactor。在复杂算法处理或类似工作的情况下,最好使用纯 Java,因为 Reactor 会增加大约 100-150% 的性能开销。
如何使用 Project Reactor 和 Reactor-Netty (WebFlux) 构建高效的 CPU 密集型任务处理
我建议遵循模式 "Queue of Work" 这样每个线程都会在前一个任务完成后接受一个新任务。
如果我们有一个 CPU 密集型任务,总是建议将其安排在专用线程池上。尽管它会增加一点开销,但我们将有更高的 I/O 读写延迟,这是任何联网应用程序不可或缺的一部分。对于Netty,我们可以确定Netty 的EventLoop 只对网络进行读写,仅此而已。
要在专用线程池上安排任务,我们可以遵循下面代码示例中显示的技术:
@PostMapping
public Mono<CpuIntensiveResult> cpuIntensiveProcessingHandler(
Mono<CpuIntensiveInput> monoInput
) {
return monoInput
.publishOn(Schedulers.fromExecutorService(myOwnDedicatedExecutor))
.map(i -> doCpuIntensiveInImperativeStyle(i));
}
正如我们从上面的代码中看到的,使用 Project Reactor 武器库中的一个运算符,我们可以轻松地在专用线程池上安排工作处理。反过来,我们可以快速将任何现有的 Executor Service 包装到 Scheduler 中,并使用 whit-in Reactor 生态系统
阻塞多线程技术怎么样?
通常情况下,线程多于内核的多线程技术不会给我们带来任何好处。这里的缺点是相同的——上下文切换和争用。这些任务似乎是同时处理的。但是,系统调度程序会在并发线程之间进行 CPU 时间分配。它将在几个密集型任务之间共享相同的 CPU,因此最终会导致每个任务的延迟更高。平均而言,处理时间将高于系统中与 CPUs/Cores 相同数量的线程完成相同工作的时间。
一些关于线程模型作为 "true" 处理模型的注释。
根据提到的白皮书,线程模型是一个真正的编程模型。我猜,这篇论文的作者正在谈论 Green Threads。 Green Threads 可能是更好的编程模型,但最终,您将不得不遵循上面提到的 Reactor 编程模型的相同规则。 Thread 和随后的命令式编程模型的缺点是无法 处理数据流,而 Reactor 编程模型非常适合。
此外,我建议重新访问 Universal Scalability Law 并审查争用和连贯性问题(与当前 Java 线程执行相关)。
此外,following paper.
中对可伸缩性进行了很好的概述。
另一个高效使用异步 + 非阻塞请求处理的示例是 Facebook 架构,它在选择负载时将工作队列转换为工作堆栈,从而保持最低延迟。
我想了解更多关于springboot webflux的底层并发模型?
对于CPU密集的网络服务,传统的阻塞多线程模型是否更适合?还是根据本文 https://people.eecs.berkeley.edu/~brewer/papers/threads-hotos-2003.pdf?
一般而言,传统线程池模型更适合吗?如果我在反应器链中有一个阻塞步骤,我会使用 publishOn 将其安排到不同的线程池。这会释放原始线程并使整个链仍然是非阻塞的吗?
WebFlux 适合的地方及其背后的工作原理
从我的角度来看,最适合 Spring WebFlux 的是网络密集型应用程序。在幕后,Spring WebFlux 使用 Reactor-Netty,它是 Netty 的 EventLoopThreadPool
创建 Runtime.getRuntime().availableProcessors() * 2
个线程。这意味着每个 Thread
都绑定到它自己的 core/processor 并且对 CPU 资源的争用最少。这种模型非常适合端到端的非阻塞通信。因此,在传入请求以远程网络调用结束的情况下,该调用应以非阻塞方式执行,以便同一线程可以返回到偶数循环队列中的其余任务。这种技术使我们能够有效地利用我们的硬件,几乎没有花费在上下文切换和高争用上的开销,这是与大量相关线程阻塞通信的常见 属性。
Project Reactor 的作用
Project Reactor 在 SpringWebFlux 中的核心作用是提供一种编程模型,以保持复杂的非阻塞、异步执行的清晰度。它隐藏了数据处理延续的复杂性,并允许我们轻松构建一个功能性的、声明性的元素处理管道。此外,Reactor 的线程模型只是几个运算符,可以在专用线程池上轻松进行复杂而高效的元素处理重新安排。在引擎盖下,使用从 Java 核心库中获取的相同 ThreadPools 和 ExecutorServices。
如何处理 CPU 密集型任务,Project Reactor 适合吗?
我会说 - Reactor Netty 也非常适合 CPU 密集型任务。但在那种情况下,应适当使用 Project Reactor。在复杂算法处理或类似工作的情况下,最好使用纯 Java,因为 Reactor 会增加大约 100-150% 的性能开销。
如何使用 Project Reactor 和 Reactor-Netty (WebFlux) 构建高效的 CPU 密集型任务处理
我建议遵循模式 "Queue of Work" 这样每个线程都会在前一个任务完成后接受一个新任务。
如果我们有一个 CPU 密集型任务,总是建议将其安排在专用线程池上。尽管它会增加一点开销,但我们将有更高的 I/O 读写延迟,这是任何联网应用程序不可或缺的一部分。对于Netty,我们可以确定Netty 的EventLoop 只对网络进行读写,仅此而已。
要在专用线程池上安排任务,我们可以遵循下面代码示例中显示的技术:
@PostMapping
public Mono<CpuIntensiveResult> cpuIntensiveProcessingHandler(
Mono<CpuIntensiveInput> monoInput
) {
return monoInput
.publishOn(Schedulers.fromExecutorService(myOwnDedicatedExecutor))
.map(i -> doCpuIntensiveInImperativeStyle(i));
}
正如我们从上面的代码中看到的,使用 Project Reactor 武器库中的一个运算符,我们可以轻松地在专用线程池上安排工作处理。反过来,我们可以快速将任何现有的 Executor Service 包装到 Scheduler 中,并使用 whit-in Reactor 生态系统
阻塞多线程技术怎么样?
通常情况下,线程多于内核的多线程技术不会给我们带来任何好处。这里的缺点是相同的——上下文切换和争用。这些任务似乎是同时处理的。但是,系统调度程序会在并发线程之间进行 CPU 时间分配。它将在几个密集型任务之间共享相同的 CPU,因此最终会导致每个任务的延迟更高。平均而言,处理时间将高于系统中与 CPUs/Cores 相同数量的线程完成相同工作的时间。
一些关于线程模型作为 "true" 处理模型的注释。
根据提到的白皮书,线程模型是一个真正的编程模型。我猜,这篇论文的作者正在谈论 Green Threads。 Green Threads 可能是更好的编程模型,但最终,您将不得不遵循上面提到的 Reactor 编程模型的相同规则。 Thread 和随后的命令式编程模型的缺点是无法 处理数据流,而 Reactor 编程模型非常适合。
此外,我建议重新访问 Universal Scalability Law 并审查争用和连贯性问题(与当前 Java 线程执行相关)。 此外,following paper.
中对可伸缩性进行了很好的概述。另一个高效使用异步 + 非阻塞请求处理的示例是 Facebook 架构,它在选择负载时将工作队列转换为工作堆栈,从而保持最低延迟。