Springboot Webflux反​​应器并发模型

时间:2018-12-07 19:32:55

标签: spring-boot project-reactor reactor-netty

我想了解更多有关springboot webflux的底层并发模型的信息?

对于CPU密集型Web服务,传统的阻塞多线程模型是否更合适?还是根据本文https://people.eecs.berkeley.edu/~brewer/papers/threads-hotos-2003.pdf,在一般的传统线程池模型中是否更合适?

如果我在反应堆链中有阻塞步骤,则可以使用publishOn将其调度到其他线程池。这样是否可以释放原始线程并使整个链仍然畅通无阻?

1 个答案:

答案 0 :(得分:4)

WebFlux适合何处以及如何在引擎盖下工作

从我的角度来看,最适合Spring WebFlux的是网络密集型应用程序。在幕后,Spring WebFlux使用Reactor-Netty,它是Netty的backpressure-supported包装器。 Reactor-Netty使用几乎相同的Netty线程策略,并为其内部Runtime.getRuntime().availableProcessors() * 2创建EventLoopThreadPool线程。这意味着每个Thread都绑定到其自己的内核/处理器,并且在争用CPU资源时工作最少。这种模型在端到端的非阻塞通信中非常有效。因此,在传入请求最终以远程网络调用结束的情况下,该调用应以非阻塞方式执行,以便同一线程可以返回到偶数循环队列中的其余任务。这种技术使我们能够有效利用硬件,而几乎没有在上下文切换和争用上花费任何开销,这是阻止与大量相关线程进行通信的常见特性。

项目反应堆的作用

Project Reactor在Spring WebFlux中的核心作用是提供一种编程模型,该模型可以保持复杂的非阻塞,异步执行的清晰度。它隐藏了数据处理连续性的复杂性,并使我们能够轻松地构建功能性的声明式元素处理管道。而且,Reactor的线程模型只是几个运算符,它们使复杂而高效的元素处理能够在专用线程池上进行重新调度而不会造成麻烦。在后台使用与Java Core库中相同的ThreadPools和ExecutorServices。

如何处理CPU密集型任务,Project Reactor是否适合在那里?

我会说-Reactor Netty也非常适合CPU密集型任务。但在这种情况下,应适当使用Project Reactor。在复杂的算法处理或类似工作的情况下,最好使用纯Java,因为Reactor在性能方面会增加大约100-150%的开销。

如何使用Project Reactor和Reactor-Netty(WebFlux)构建高效的CPU密集型任务处理

我建议遵循“工作队列”模式,以便每个线程在上一个任务完成后将承担一个新任务。

如果我们有占用大量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武器库中的一名操作员,我们可以轻松地在专用Threadpool上安排工作处理。反过来,我们可以将任何现有的Executor服务快速包装到Scheduler中,并使用whit-in Reactor生态系统

如何阻止多线程技术?

通常,多线程技术的线程多于内核,我们将不会获得任何好处。这里的缺点是相同的-上下文切换和竞争。似乎任务是同时处理的。但是,系统调度程序会在并发线程之间完成CPU时间分配方面的相同工作。它将在多个密集任务之间共享相同的CPU,因此最终导致每个任务的延迟增加。平均而言,处理时间要比使用与系统中CPU /核心相同数量的线程完成相同工作的时间更长。

关于将线程模型作为“真实”处理模型的一些注意事项。

根据上述白皮书,线程模型是真正的编程模型。我想,本文的作者正在谈论Green Threads。 Green Threads可能是更好的编程模型,但是最后,您将必须遵循上面针对Reactor编程模型提到的相同规则。线程和随后的命令式编程模型的缺点是无法使用数据流,而Reactor编程模型非常适合。

此外,我建议重新访问Universal Scalability Law并回顾争用和一致性的问题(这与当前的Java线程执行有关)。 另外,following paper中对可伸缩性进行了很好的概述。

有效利用异步+非阻塞请求处理的另一个示例是Facebook体系结构,该体系结构在拾取负载时将工作队列转换为工作堆栈,从而可以保持最低的延迟。