剖析Spring WebFlux反應(yīng)式編程設(shè)計(jì)及工作原理
前言
Spring 5發(fā)布有兩年了,隨Spring 5一起發(fā)布了一個(gè)和Spring WebMvc同級(jí)的Spring WebFlux。這是一個(gè)支持反應(yīng)式編程模型的新框架體系。反應(yīng)式模型區(qū)別于傳統(tǒng)的MVC最大的不同是異步的、事件驅(qū)動(dòng)的、非阻塞的,這使得應(yīng)用程序的并發(fā)性能會(huì)大大提高,單位時(shí)間能夠處理更多的請(qǐng)求。這里不講WebFlux是怎么用的,有什么用,這類文章網(wǎng)上有太多了,而且都寫的非常不錯(cuò)。下面主要看下WebFlux是怎么從無(wú)到有,框架怎么設(shè)計(jì)的,已期能夠更靈活的使用WebFlux。
接口抽象
Spring最牛逼的地方就是,無(wú)論啥東西,都可以無(wú)縫的集成到Spring。這得益于Spring體系優(yōu)良的抽象封裝能力。WebFlux框架也一樣,底層實(shí)現(xiàn)其實(shí)不是Spring的,它依賴reactor和netty等。Spring做的就是通過(guò)抽象和封裝,把reactor的能力通過(guò)你最熟悉不過(guò)的Controller來(lái)使用。而且不局限于此,除了支持和Spring Mvc一樣的控制器編碼模式,還支持路由器模式(RouterFunctions),還支持端點(diǎn)模式(EndPoint)等。WebFlux所有功能其實(shí)內(nèi)部只由幾個(gè)抽象類構(gòu)建而成:
org.springframework.boot.web.reactive.server.ReactiveWebServerFactory
org.springframework.boot.web.server.WebServer
org.springframework.http.server.reactive.HttpHandler
org.springframework.web.reactive.HandlerMapping
org.springframework.web.server.WebHandler
WebServer
我們從最底層往上層剖析,WebServer見(jiàn)名之意,就是Reacive服務(wù)器的抽象類,它定義了服務(wù)的基本方法行為,包含啟動(dòng),停止等接口。結(jié)構(gòu)如下:
public interface WebServer { void start() throws WebServerException; void stop() throws WebServerException; int getPort(); }
Spring默認(rèn)有五個(gè)WebServer的實(shí)現(xiàn),默認(rèn)的不特別指定情況下,在spring-boot-starter-webflux自帶的是Netty的實(shí)現(xiàn),其實(shí)現(xiàn)類如下:
ReactiveWebServerFactory
對(duì)應(yīng)WebServer,每個(gè)實(shí)現(xiàn)都會(huì)有一個(gè)工廠類對(duì)應(yīng),主要準(zhǔn)備創(chuàng)建WebServer實(shí)例的資源,如NettyReactiveWebServerFactory生產(chǎn)WebServer方法:
public WebServer getWebServer(HttpHandler httpHandler) { HttpServer httpServer = createHttpServer(); ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter( httpHandler); return new NettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout); }
可以看到,在創(chuàng)建WebServer實(shí)例時(shí),傳入了一個(gè)入?yún)ⅲ琀ttpHandler。而且進(jìn)而傳入了一個(gè)HttpHandlerAdapter實(shí)例里,這是因?yàn)槊總€(gè)WebServer的接收處理接口的適配器是不一樣的,在每個(gè)不同的WebServer工廠里通過(guò)不過(guò)的適配器去適配不同的實(shí)現(xiàn)。最后轉(zhuǎn)化成統(tǒng)一設(shè)計(jì)的HttpHandler里,見(jiàn)下面。
HttpHandler
接下來(lái)看下HttpHandler,上面在創(chuàng)建WebServer的時(shí)候,傳了一個(gè)入?yún)?,類型就是Httphandler。為了適配不同的WebServer請(qǐng)求響應(yīng)體,Spring設(shè)計(jì)了HttpHandler用來(lái)轉(zhuǎn)化底層的Http請(qǐng)求響應(yīng)語(yǔ)義,用來(lái)接收處理底層容器的Http請(qǐng)求。其結(jié)構(gòu)如下:
public interface HttpHandler { Monohandle(ServerHttpRequest request, ServerHttpResponse response); }
如在Netty的實(shí)現(xiàn)中,Netty接收請(qǐng)求處理的適配器ReactorHttpHandlerAdapter的apply中轉(zhuǎn)化的偽代碼如下:
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其實(shí)一般來(lái)講設(shè)計(jì)到HttpHandler這一層級(jí)基本就差不多了,有一致的請(qǐng)求體和響應(yīng)體了。但是Spring說(shuō)還不夠,對(duì)Web開(kāi)發(fā)來(lái)講不夠簡(jiǎn)潔,就又造了一個(gè)WebHandler,WebHandler架構(gòu)更簡(jiǎn)單,如下:
public interface WebHandler { Monohandle(ServerWebExchange exchange); }
這回夠簡(jiǎn)潔了,只有一個(gè)入?yún)?,那?qǐng)求提和響應(yīng)體去哪里了呢?被包裝到ServerWebExchange中了。我么看下當(dāng)HttpHandler接收到請(qǐng)求后,是怎么處理然后在調(diào)用WebHandler的,最終處理HttpHandler實(shí)現(xiàn)是HttpWebHandlerAdapter.java,通過(guò)其內(nèi)部的createExchange方法將請(qǐng)求和響應(yīng)體封裝在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的構(gòu)造,可以看到就是根據(jù)web交換器返回了一個(gè)Handler對(duì)象
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); }
上面的“請(qǐng)求“已經(jīng)到WebHandler了,那么最終是怎么到我們定義的控制器接口的呢?其實(shí),沒(méi)有HandlerMapping,Spring WebFlux的功能也是完整的,也是可編程的,因?yàn)榭梢曰赪ebHandler直接編碼。我們最弄的一個(gè)網(wǎng)關(guān)最后就是直接走自定義的WebHandler,根本沒(méi)有HandlerMapping的什么事情,但是你這么做的話就失去了Spring編碼的友好性了。WebFlux的初始化過(guò)程中,會(huì)去Spring上下文中找name是“webHandler”的的WebHandler實(shí)現(xiàn)。默認(rèn)情況下,Spring會(huì)在上下文中初始化一個(gè)DispatcherHandler.java的實(shí)現(xiàn),Bean的name就是“webHandler”。這個(gè)里面維護(hù)了一個(gè)HandlerMapping列表,當(dāng)請(qǐng)求過(guò)來(lái)時(shí)會(huì)迭代HandlerMapping列表,返回一個(gè)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的內(nèi)部結(jié)構(gòu)如下:
上面箭頭指向的地方說(shuō)明了為什么WebFlux支持控制器和路由器模式模式的編碼,因?yàn)樗麄兎謩e有實(shí)現(xiàn)的HandlerMapping,能夠在WebHandler的handler里路由到具體的業(yè)務(wù)方法里。紅框中正是通過(guò)@Controller和@ResultMaping定義的接口信息。
啟動(dòng)流程分析
上面介紹了五個(gè)主要的抽象接口定義,以及功能。這五個(gè)接口在Spring WebFlux里是靈魂一樣的存在。不過(guò)想要徹底的搞懂Web Flux的設(shè)計(jì)以及實(shí)現(xiàn)原理,僅僅了解上面這些接口定義是遠(yuǎn)遠(yuǎn)不夠的,看完上面接口的分析肯定有中模糊的似懂非懂的感覺(jué),不著急,接下來(lái)分析下,在Spring Boot環(huán)境中,Spring WebFlux的啟動(dòng)流程。
ReactiveWebServerApplicationContext
WebFlux的啟動(dòng)都在Reactive的上下文中完成,和WebMvc類似,Mvc也有一個(gè)ServletWebServerApplicationContext,他們是同宗同脈的。
ReactiveWebServerApplicationContext還有一個(gè)父類AnnotationConfigReactiveWebServerApplicationContext
在Spring boot啟動(dòng)中,創(chuàng)建的就是這個(gè)父類的實(shí)例。
在Spring boot的run()方法中創(chuàng)建上下文時(shí)有如下代碼:
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); }
可以看到,當(dāng)webApplicationType是REACTIVE時(shí),加載的就是DEFAULT_REACTIVE_WEB_CONTEXT_CLASS。webApplicationType類型是通過(guò)識(shí)別你加載了哪個(gè)依賴來(lái)做的。熟悉Spring啟動(dòng)流程的同學(xué)都知道,基礎(chǔ) 的Spring上下文是在AbstractApplicationContext的refresh()方法內(nèi)完成的,針對(duì)不同的上下文文實(shí)現(xiàn)實(shí)例還會(huì)有一個(gè)onRefresh()方法,完成一些特定的Bean的實(shí)例化,如WebFlux的上下文實(shí)例就在onRefresh()中完成了WebServer的創(chuàng)建:
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里面啟動(dòng)流程太復(fù)雜,全盤脫出寫的太長(zhǎng)嚴(yán)重影響閱讀體驗(yàn)。所以上面權(quán)當(dāng)拋磚引玉,開(kāi)一個(gè)好頭。不過(guò),WebFlux的啟動(dòng)流程節(jié)點(diǎn)博主都已分析并整理成流程圖了,結(jié)合上面的接口設(shè)計(jì)分析,搞懂WebFlux的設(shè)計(jì)及工作原理應(yīng)該冒點(diǎn)問(wèn)題
以上就是剖析Spring WebFlux反應(yīng)式編程模型工作原理的詳細(xì)內(nèi)容,更多關(guān)于Spring WebFlux反應(yīng)式編程模型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java動(dòng)態(tài)線程池插件dynamic-tp集成zookeeper
ZooKeeper是一個(gè)分布式的,開(kāi)放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),是Google的Chubby一個(gè)開(kāi)源的實(shí)現(xiàn),是Hadoop和Hbase的重要組件。它是一個(gè)為分布式應(yīng)用提供一致性的軟件,提供的功能包括:配置維護(hù)、域名服務(wù)、分布式同步、組服務(wù)等2023-03-03Java連接mysql數(shù)據(jù)庫(kù)的詳細(xì)教程(推薦)
這篇文章主要介紹了Java連接mysql數(shù)據(jù)庫(kù)的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08JAVA數(shù)字千分位和小數(shù)點(diǎn)的現(xiàn)實(shí)代碼(處理金額問(wèn)題)
這篇文章主要介紹了JAVA數(shù)字千分位和小數(shù)點(diǎn)的現(xiàn)實(shí)代碼(處理金額問(wèn)題),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10java使用Socket類接收和發(fā)送數(shù)據(jù)
Socket類是負(fù)責(zé)處理客戶端通信的Java類。本文主要是介紹java使用Socket類接收和發(fā)送數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-10-10創(chuàng)建Jersey REST 服務(wù),基于Maven的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇?jiǎng)?chuàng)建Jersey REST 服務(wù),基于Maven的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06SpringCloud2020版本配置與環(huán)境搭建教程詳解
這篇文章主要介紹了SpringCloud2020版本配置與環(huán)境搭建教程詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12