webflux的并发模型
webflux Concurrency Model 并发模型
spring mvc和webflux都支持注解controller开发,但是底层的并发模型完全不一样,对线程阻塞的预期假设也不一样。
spring mvc或者说servlet应用里边,servlet是单例多线程模型,是假定业务代码要阻塞工作线程的,例如业务代码里向远程服务发起了一个同步阻塞的http调用。所以servlet容器需要使用一个大线程池去处理并发的请求、应对这种潜在的阻塞。因为不知道哪个请求对应的处理过程中会有这种阻塞,所以线程池小了的话,都被阻塞了、就没有空闲线程去用了。
而在webflux里边、或者说non-blocking服务器,我们是预期或者说要求业务代码都不应该是阻塞的。因此,non-blocking服务器都是用一个小的、固定数目线程的线程池去处理请求。这里边的线程,也称为event loop worker。
系统的可伸缩型scale和“小且fixed-size线程池”听起来有些矛盾,但是其实如果能保证绝对不阻塞线程来获得结果、而是依靠回调来获得结果,那么我们其实是不需要额外的线程的,因为cpu并发本质上是分配时间片给各个线程,各个线程来回切换来执行,而效率最高的方式显然是并行,每个cpu负责一个线程。我们是因为线程处理过程中需要阻塞,这时候cpu等着没事干,才多分配几个线程,好让cpu忙起来,提高其利用率的。
|
如果确实需要使用阻塞库咋办?Reactor和RxJava都提供了publishOn方法,可以启动一个额外的线程异步处理业务逻辑。这招虽然简单,但是要记住,一但真的有这种情况了,说明你技术选型的时候可能不应该或者说至少不一定要选webflux,阻塞API库不适合webflux这种并发模型。
关于线程安全性,在Reactor和RxJava里边,都是通过operator来声明业务逻辑的。在运行的时候,数据都是在反应式pipeline里边、分阶段的进行串行处理的。所以说应用程序员不需要去考虑线程安全问题、不需要去想办法保护共享资源(mutable state),因为pipeline中的业务代码不会同时被多个线程并发的调用。
关于线程模型,在一个跑着webflux的server上有哪些主要的线程呢?
1、一个普通的、比如没有什么data access或者其他的类似依赖的webflux server上边,会有一个server线程,以及cpu核心数个请求处理线程。而servlet容器就不一样了:线程数要多得多,像tomcat这种默认最大线程数来到了200个,因为它要同时支持servlet blocking IO和servlet3.1的non-blocking IO。
2、WebClient也是用的event loop方式:少量的几个名字“reactor-http-nio-
xx”的这种线程来负责发送和读请求的返回。但是如果在一个webflux应用里边,同时有WebServer模块的服务端和WebClient的客户端,并且底层都是用的Reactor Netty的话,那么这两者是共用event loop资源的。
3、Reactor和RxJava提供了线程池的抽象————schedulers,使用publishOn方法把业务代码丢给一个额外异步线程池————scheduler去处理、而不在event loop里处理了。
类似于JDK里边的Executors类里边的那几个静态方法:cacheThreadPool、fixThreadPool、balabala。。。scheduler也有不同的并发策略,适合不同的场景:
“parallel”,需求少量的几个线程、cpu计算密集型场景;
“elastic”,需求大量的线程、IO密集型场景。
所以如果你瞅见了上述这类名字的线程,意味着当前这个应用使用了Scheduler启了这些策略的异步线程池。
4、Data access库和其他第三方的依赖可以创建并使用它们自己的线程池。
加了一些自己的理解。