什么是WebFlux? Spring WebFlux 是一套全新的 Reactive Web 栈技术,实现完全非阻塞,支持 Reactive Streams 背压等特性,并且运行环境不限于 Servlet 容器(Tomcat、Jetty、Undertow),如 Netty 等。Spring WebFlux 与 Spring MVC 可共存,在 Spring Boot 中,Spring MVC 优先级更高。为什么优先级高,大家可以看看我之前的文章https://blog.csdn.net/mikezzmeric/article/details/87209555
我们为什么要使用web flux呢?Spring官方讲了两个主要原因,第一点是servlet API部分是阻塞式的,而且Listner和Filter是同步的方式。这个原因其实是错误的,我们早就知道Servlet在3.1的时候就已经可以异步的方式返回结果了。第二点原因是函数式编程的出现,这个原因可以算,但是不完全。所以个人觉得原因如下:从 Spring MVC 注解驱动的时代开始,Spring 官方有意识地去 Servlet 化。不过在 Spring MVC 的时代,Spring扔拜托不了 Servlet 容器的依赖,然而 Spring 借助 Reactive Programming 的势头,WebFlux 将 Servlet 容器从必须项变为可选项,并且默认采用 Netty Web Server 作为基础,从而组件地形成 Spring 全新技术体系,包括数据存储等技术栈:
Webflux的实现可以是函数式的实现也可以是注解式的实现,我们看一个例子:
函数映射:
RouterFunction<ServerResponse> route =route(GET(“/person/{id}”).and(accept(APPLICATION_JSON)), handler::getPerson).andRoute(GET(“/person”).and(accept(APPLICATION_JSON)), handler::listPeople).andRoute(POST(“/person”), handler::createPerson);
对应的注解映射如下:
@GetMapping(value=”/person/{id}”,consumes=APPLICATION_JSON)public void getPerson(HttpServletRequest request,HttpServletResponse) {}@GetMapping(value=”/person”,consumes=APPLICATION_JSON)public void listPeople(HttpServletRequest request,HttpServletResponse) {}@PostMapping(value=”/person”)public void createPerson(HttpServletRequest request,HttpServletResponse) {}
实现的功能一样,方式呢有所区别。
Spring MVC 和 Spring WebFlux 均能使用注解驱动 Controller,然而不同点在于并发模型和阻塞特性。Spring MVC 通常是Servlet 应用,因此,可能被当前线程阻塞。以远程调用为例,由于阻塞的缘故,导致 Servlet容器使用较大的线程池处理请求。Spring WebFlux 通常是非阻塞服务,不会发生阻塞,因此该阻塞服务器可使用少量、固定大小的线程池处理请求。
接下来介绍一下WebFlux的核心组件:
1.HttpHandler
是一种带有处理 HTTP 请求和响应方法的简单契约。
2.WebHandler
Bean 名称Bean类型数量描述 WebExceptionHandler0..NProvide handling for exceptions from the chain of WebFilter ‘s and the target WebHandler WebFilter0..NApply interception style logic to before and after the rest of the filter chain and the target WebHandler .webHandlerWebHandler1The handler for the request.webSessionManagerWebSessionManager0..1The manager for WebSession ‘s exposed through a method on ServerWebExchange . DefaultWebSessionManager by default.serverCodecConfigurerServerCodecConfigurer0..1For access to HttpMessageReader ‘s for parsing form data and multipart data that’s then exposed through methods on ServerWebExchange .
ServerCodecConfigurer.create() by default.localeContextResolverLocaleContextResolver0..1The resolver for LocaleContext exposed through a method on ServerWebExchange . AcceptHeaderLocaleContextResolver by default.
webHandler显得有一些抽象,我们可以通过对比SpringMVC的一些组件帮助大家理解一下在WebFlux中各个组件的作用:
核心组件Spring Web MVCSpring WebFlux前端控制器(Front Controller)DispatcherServletDispatcherHandlerHandler 请求映射o.s.w.servlet.HandlerMappingo.s.w.reactive.HandlerMappingHandler 请求适配器o.s.w.servlet.HandlerAdaptero.s.w.reactive.HandlerAdapterHandler 异常处理器o.s.w.servlet.HandlerExceptionResolverHandlerResult.exceptionHandler视图处理器o.s.w.servlet.ViewResolvero.s.w.reactive.r.v.ViewResolverLocale 解析器o.s.w.servlet.LocaleResolver LocaleContextResolver@Enable 模块注解@EnableWebMvc@EnableWebFlux自定义配置器WebMvcConfigurerWebFluxConfigurer内容协商配置器ContentNegotiationConfigurerRequestedContentTypeResolverBuilder内容协商管理器ContentNegotiationManager无内容协商策略ContentNegotiationStrategyRequestedContentTypeResolver资源跨域注册器o.s.w.servlet.c.a.CorsRegistryo.s.w.reactive.c.CorsRegistryHanderMethod 参数解析器o.s.w.m.s.HandlerMethodArgumentResolvero.s.w.reactive.r.m.HandlerMethodArgumentResolverHanderMethod 返回值解析器HandlerMethodReturnValueHandlerHandlerResultHandler
接下来我们看一下采用函数式的方式编写的时候,一个请求的处理流程是怎样的:
RouterFunctionMapping中有private RouterFunction<?> routerFunction;这里面表面看起来只有一个Bean,其实它里面组合了非常多的RouterFunction,它是如何根据用户的请求找到对应的Function的呢?
@Overrideprotected Mono<?> getHandlerInternal(ServerWebExchange exchange) {if (this.routerFunction != null) {ServerRequest request = ServerRequest.create(exchange, this.messageReaders);return this.routerFunction.route(request).doOnNext(handler -> setAttributes(exchange.getAttributes(), request, handler));}else {return Mono.empty();}}
关键部分就是通过它的成员变量routerFunction的route方法来找,其实就是通过用户写的predicate来判断请求是否相符合,如果符合就返回一个Mono<HandlerFunction<T>>
WebFlux的使用场景,我们根据一些测试报告来看看:
1.性能考虑:
1.1WebFlux提升的其实往往是系统的伸缩性,对于速度的提升没有太多的明显。所以它不适合注入RPC这类对RT(响应时间)要求较高的应用。
1.2关注编程用户友好性,Reactive 编程尽管没有新增大量的代码,然而编码(和调试)却是变得更为复杂
1.3现在面临的最大问题是缺少文档。在生成测试应用中,它已经给我们造成了最大障碍,并使得我们可能已经缺少了关键点。因此,我们并不会太快地投入 Reactive 编程,同时等待关于它的更多反馈。因此,Spring WebFlux 尚未证明自身明显地优于Spring MVC。
2.编程模型
注解驱动编程模型
函数式编程模型
到底选择哪一个呢?
如果SpringMVC应用工作的挺好的,就没必要切换成webflux。
如果你想使用非阻塞的技术栈,可以考虑使用WebFlux。当然也不一定非要webflux,servlet3.1之后我们也知道提供了非阻塞的方式。
如果想使用轻量级的函数式编程,可以考虑使用函数式编程模型。
3.并发模型适用性
如果你的请求不需要实时的返回,那么可以使用异步的方式。
如果RT敏感,不要使用异步模型,因为涉及到线程切换或者等待可能引发超时。