SpringBoot Webflux 解析
1. Webflux 介绍
Webflux 是一种异步非阻塞的 IO 模型,当有请求过来时,它会将请求交由 worker 线程去处理,这样就可以极大的提升吞吐量,所以他比较适合用于 IO 密集型的场景。
web flux 虽然可以给我带来吞吐量的提升,但是同时也带了一些问题,如调试困难,有一定的学习成本,此外目前仅有少数数据库支持异步非阻塞查询,如 redis 支持,而 mysql 不支持。它使用 netty 作为服务端框架,网络通信性能很高,但是我们在准备引入该技术的时候也需要十分谨慎。
在 SpringBoot 官方也给出了相关建议:
如果你的项目目前使用的时 SpringMvc 且运行良好那就没有必要使用 Webflux,SpringMvc 的编程方式易编写也易理解,同时还有大量的类库可供使用。
如果你已经使用了异步非阻塞的编程方式,那么可以切换到 webflux,webflux 提供了更加丰富的功能
如果你对轻量级、java8 的 lambdas 函数式编程的 web 框架感兴趣,那么在一些小型项目上可以使用。
在微服务架构下,你可以同时使用 springmvc 和 wenflux,它们的在基础注解模型上是一样的
如果你依赖了持久化层的 API(JDBC、JPA)或者网络 API,对于基础架构来说 springmvc 是最好的选择
如果你的 springmvc 应用调用远程服务,可以尝试使用 webclient。你可以直接从 controller 方法中获取到 reactive 类型。
如果你在一个大型团队,那么要谨慎考虑切换的成本,webflux 的学习曲线还是比较陡峭。
2. Reactor 框架介绍
Springboot 官网为我们提供了关于 reactive 的技术栈,如下图所示:

Webflux 主要技术依赖有:Reactive Streams 反应式编程标准和规范、基于 reactive streams 的反应式编程框架和以 reactive 为基础实现 web 领域的反应式编程框架。
Reactive Streams 是一套基于 jvm 面向流式类库的标准和规范,它要求:
具有处理无限数量数据的能力
按序处理数据
异步非阻塞的传递数据
必须实现非阻塞的背压
Reactive api 规范组件:
Publisher:数据发布者
Subscriber:数据订阅者
Subscription:订阅信号
Processor:处理器(包含了发布者和订阅者的混合体)
Reactive 操作符:
Map 操作符:对每一个数据进行转换操作,如,每个数据除 2,当处理完交由消费端。

Flatmap 操作符:有时候我们一个元素需要映射成一个元素序列,将该序列压平然后合并成更大的元素序列。

Filter 操作符:指将不符合条件的元素过滤掉,不发给消费端。

Zip 操作符:将两个 publisher 的序列合并输出。

Reactor 与 java 8 Stream 区别:虽然从写法来看感觉一样,其实是形似神不似。Reactor 采用的是 push 模式,服务端推送数据给客户端消费,它是异步反应式模式;而 stream 是 pull 模式是同步的命令式程序。
Reactor 线程模型:
创建线程方式:
Schedulers.immediate():当前线程
Schedulers.single():可重用的单线程
Schedulers.elastic():弹性线程池
Schedulers.parallel():固定大小线程池
Schedulers.fromExecutorService():自定义线程池
线程切换总结:
1. PublishOn:它将上游信号传给下游,同时改变后续的操作符的执行所在线程,直到下一个 publishOn 出现在这个链上。
2. SubscribeOn:作用于向上的订阅链,无论处于操作链的什么位置,他都会影响到源头的线程执行环境,但不会影响后续的 publishOn。
3.Webflux 处理流程解析
首先我们看一下 SpringMvc 的请求处理流程,当请求进来之后会进入到 DispatcherServlet,DispatcherServlet 会将请求转发给 handlerMapper 找到映射器,之后通过 HandlerAdapter 找到对应的 handler 处理请求,处理完之后会调用 ViewResolver 视图解析器对结果进行解析,然后通过 View 视图解析器进行渲染。在 Webflux 中也存在类似 DispatcherServlet 的存在,那就是 DispatcherHandler。

DispatcherHandler 类中的核心方法是 handle,该方法的参数是 ServerWebExchange,该类的作用是 Http 请求和响应相互的纽带,该类中不仅提供了获取 ServerHttpRequest 和 ServerHttpResponse,而且还为服务端处理请求时提供了相关的属性的方法,如获取 request attribute、session 等,handler 处理的主要流程是先获取 handlerMappings,然后调用 concatMap 方法获取对应的 HandlerMapping,该方法是保证顺序的,如果在 controller 和 RouterFunction 同时存在相同的 path,那么只会执行第一个,也就是 routerFunction 的 handler,然后执行该方法并处理返回的结果。

DispatcherHandler 实现了 ApplicationContextAware 接口,在 setApplicationContext 方法中调用了 initStrategies 方法,该方法完成了 DispatcherHandler 的准备工作。
该方法首先获取所有的 HandlerMapping 实现类,有三类,分别是 requestMappingHandlerMapping 主要处理 SpringMvc 注解方式;第二个是 routerFunctionMapping,这个是实现 RouterFunction 接口,通过此种方式配置路径映射的关系;第三个是 resourceHandlerMapping,其类型为 SimpleUrlHandlerMapping,主要处理通过 url 匹配路径,如/webjars/**、/**相关的 ResourceWebHandler。

其次获取 HandlerAdapter 的实现类,Handler 主要是根据 @RequestMapping 的注解配置的 path 找到对应的方法,然后执行此方法。

最后获取所有实现了 HandlerResultHandler 接口的子类,共有 4 种实现,ResponseEntityResultHandler 主要处理 HttpEntity 和 ResponseEntity;ResponseBodyResultHandler 主要处理 @ResponseBody 标注的返回结果;ViewResolutionResultHandler 主要处理返回值为 void、String、View、Model、Map、Rendering 和 ModelAndView。

因此,RouterFucntion 实现了 InitilizationBean,所以先会调用 afterPropertiesSet 方法,整个初始化流程为:

至此,完成了 Webflux 的准备工作,那么接下来看下对请求的处理流程:

4.总结
web Flux 采用异步非阻塞模型,相比于 SpringMvc 可以极大的提升吞吐量。但是也不是没有副作用,像学习曲线高、调试难以及不支持 jdbc 等数据库,而 NoSql 数据库相对支持较为完善,而且它可以和 SpringMvc 一起使用。
WebFlux 采用 reactor 作为响应式编程框架。Reactor 采用异步非阻塞式模型,而非同步命令式编程。
版权声明: 本文为 InfoQ 作者【Ethan】的原创文章。
原文链接:【http://xie.infoq.cn/article/1fb43fa2e3599c0c9a732728e】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论