webflux响应式编程,spring-webflux
00-1010前言接口抽象web serverwebserveractoryhttphandler启动流程分析ReactiveWebServerApplicationContext
目录
Spring 5发布两年了,随Spring 5发布了一个与Spring WebMvc同级别的Spring WebFlux。这是一个支持反应式编程模型的新框架系统。反应式模型与传统MVC最大的区别是异步、事件驱动、非阻塞,大大提高了应用的并发性能,单位时间可以处理更多的请求。我这里不说WebFlux是怎么用的,有什么用。网上这种文章太多了,都写得很好。我们先来看看WebFlux是如何从无到有成长起来的,框架是如何设计的,期望能够更灵活的使用WebFlux。
前言
Spring最大的好处就是一切都可以无缝集成到Spring中。这得益于Spring系统优秀的抽象封装能力。WebFlux框架也是如此。底层实现不是Spring的,而是依赖于reactor和netty等。Spring所做的就是通过抽象和封装,通过你最熟悉的控制器来使用reactor的能力。而且,还不仅限于此。除了与Spring Mvc相同的控制器编码模式,它还支持RouterFunctions和EndPoint模式。事实上,WebFlux的所有功能都是由几个抽象类构建的:
org . spring framework . boot . web . reactive . server . reactivewebserverfactory
org . spring framework . boot . web . server . web server
org . spring framework . http . server . reactive . httphandler
org . spring framework . web . reactive . handler mapping
org . spring framework . web . server . web handler
00-1010我们自下而上分析。WebServer的名字意思是Reacive server的抽象类,定义了服务的基本方法行为,包括启动、停止等接口。其结构如下:
公共接口WebServer {void start()抛出WebServerExceptionvoid stop()引发WebServerExceptionint getPort();}Spring默认有五个WebServer的实现。当未指定默认值时,Netty的实现附带spring-boot-starter-webflux,其实现类如下:
00-1010对应WebServer,每个实现都会有一个工厂类与之对应,主要是为创建WebServer实例准备资源,比如NettyReactiveWebServerFactory生产web服务器的方法:
public web server get web server(HttpHandler HttpHandler){ http server http server=create http server();ReactorHttpHandlerAdapter handlerAdapter=新ReactorHttpHandlerAdapter(httpHandler);返回新的NettyWebServer(httpServer,handlerAdapter,this . lifecycletimeout);}如您所见,在创建WebServer实例时,传入了一个参数HttpHandler。而且引入到HttpHandlerAdapter的一个实例中,因为每个WebServer的接收和处理接口的适配器是不同的,每个不同的WebServer工厂中的适配器可以适配不同的实现。最后转化成统一设计的HttpHandler,如下图。
00-1010接下来我们来看看HttpHandler。创建web服务器时,会传递一个参数,类型为Httphandler。为了适应不同的WebServer请求响应器,Spring设计了HttpHandler来转换底层Http请求响应语义,接收和处理来自底层容器的Http请求。它的结构
如下:
public interface HttpHandler {Monohandle(ServerHttpRequest request, ServerHttpResponse response);}
如在Netty的实现中,Netty接收请求处理的适配器ReactorHttpHandlerAdapter的apply中转化的伪代码如下:
public Monoapply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) {NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(reactorResponse.alloc());try {ReactorServerHttpRequest request = new ReactorServerHttpRequest(reactorRequest, bufferFactory);ServerHttpResponse response = new ReactorServerHttpResponse(reactorResponse, bufferFactory);if (request.getMethod() == HttpMethod.HEAD) {response = new HttpHeadResponseDecorator(response);}return this.httpHandler.handle(request, response).doOnError(ex -> logger.trace(request.getLogPrefix() + "Failed to complete: " + ex.getMessage())).doOnSuccess(aVoid -> logger.trace(request.getLogPrefix() + "Handling completed"));}}
WebHandler其实一般来讲设计到HttpHandler这一层级基本就差不多了,有一致的请求体和响应体了。但是Spring说还不够,对Web开发来讲不够简洁,就又造了一个WebHandler,WebHandler架构更简单,如下:
public interface WebHandler {Monohandle(ServerWebExchange exchange);}
这回够简洁了,只有一个入参,那请求提和响应体去哪里了呢?被包装到ServerWebExchange中了。我么看下当HttpHandler接收到请求后,是怎么处理然后在调用WebHandler的,最终处理HttpHandler实现是HttpWebHandlerAdapter.java,通过其内部的createExchange方法将请求和响应体封装在ServerWebExchange中了。其handle代码如下:
public Monohandle(ServerHttpRequest request, ServerHttpResponse response) {if (this.forwardedHeaderTransformer != null) {request = this.forwardedHeaderTransformer.apply(request);}ServerWebExchange exchange = createExchange(request, response);LogFormatUtils.traceDebug(logger, traceOn ->exchange.getLogPrefix() + formatRequest(exchange.getRequest()) +(traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : ""));return getDelegate().handle(exchange).doOnSuccess(aVoid -> logResponse(exchange)).onErrorResume(ex -> handleUnresolvedError(exchange, ex)).then(Mono.defer(response::setComplete));}
HandlerMapping首先看下HandlerMapping的构造,可以看到就是根据web交换器返回了一个Handler对象
public interface HandlerMapping {String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";MonogetHandler(ServerWebExchange exchange);}
上面的请求已经到WebHandler了,那么最终是怎么到我们定义的控制器接口的呢?其实,没有HandlerMapping,Spring WebFlux的功能也是完整的,也是可编程的,因为可以基于WebHandler直接编码。我们最弄的一个网关最后就是直接走自定义的WebHandler,根本没有HandlerMapping的什么事情,但是你这么做的话就失去了Spring编码的友好性了。WebFlux的初始化过程中,会去Spring上下文中找name是webHandler的的WebHandler实现。默认情况下,Spring会在上下文中初始化一个DispatcherHandler.java的实现,Bean的name就是webHandler。这个里面维护了一个HandlerMapping列表,当请求过来时会迭代HandlerMapping列表,返回一个WebHandler处理,代码如下:
public Monohandle(ServerWebExchange exchange) {if (this.handlerMappings == null) {return Mono.error(HANDLER_NOT_FOUND_EXCEPTION);}return Flux.fromIterable(this.handlerMappings).concatMap(mapping -> mapping.getHandler(exchange)).next().switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION)).flatMap(handler -> invokeHandler(exchange, handler)).flatMap(result -> handleResult(exchange, result));}
上面mapping的内部结构如下:
上面箭头指向的地方说明了为什么WebFlux支持控制器和路由器模式模式的编码,因为他们分别有实现的HandlerMapping,能够在WebHandler的handler里路由到具体的业务方法里。红框中正是通过@Controller和@ResultMaping定义的接口信息。
启动流程分析
上面介绍了五个主要的抽象接口定义,以及功能。这五个接口在Spring WebFlux里是灵魂一样的存在。不过想要彻底的搞懂Web Flux的设计以及实现原理,仅仅了解上面这些接口定义是远远不够的,看完上面接口的分析肯定有中模糊的似懂非懂的感觉,不着急,接下来分析下,在Spring Boot环境中,Spring WebFlux的启动流程。
ReactiveWebServerApplicationContext
WebFlux的启动都在Reactive的上下文中完成,和WebMvc类似,Mvc也有一个ServletWebServerApplicationContext,他们是同宗同脉的。
ReactiveWebServerApplicationContext还有一个父类AnnotationConfigReactiveWebServerApplicationContext
在Spring boot启动中,创建的就是这个父类的实例。
在Spring boot的run()方法中创建上下文时有如下代码:
protected ConfigurableApplicationContext createApplicationContext() {Class contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}
可以看到,当webApplicationType是REACTIVE时,加载的就是DEFAULT_REACTIVE_WEB_CONTEXT_CLASS。webApplicationType类型是通过识别你加载了哪个依赖来做的。熟悉Spring启动流程的同学都知道,基础 的Spring上下文是在AbstractApplicationContext的refresh()方法内完成的,针对不同的上下文文实现实例还会有一个onRefresh()方法,完成一些特定的Bean的实例化,如WebFlux的上下文实例就在onRefresh()中完成了WebServer的创建:
protected void onRefresh() {super.onRefresh();try {createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start reactive web server",ex);}}private void createWebServer() {ServerManager serverManager = this.serverManager;if (serverManager == null) {this.serverManager = ServerManager.get(getWebServerFactory());}initPropertySources();}
文末WebFlux里面启动流程太复杂,全盘脱出写的太长严重影响阅读体验。所以上面权当抛砖引玉,开一个好头。不过,WebFlux的启动流程节点博主都已分析并整理成流程图了,结合上面的接口设计分析,搞懂WebFlux的设计及工作原理应该冒点问题
以上就是剖析Spring WebFlux反应式编程模型工作原理的详细内容,更多关于Spring WebFlux反应式编程模型的资料请关注盛行IT其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。