您好,登錄后才能下訂單哦!
這篇文章主要介紹“Spring WebFlux反應式編程設計及工作原理是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Spring WebFlux反應式編程設計及工作原理是什么”文章能幫助大家解決問題。
Spring 5發布有兩年了,隨Spring 5一起發布了一個和Spring WebMvc同級的Spring WebFlux。這是一個支持反應式編程模型的新框架體系。反應式模型區別于傳統的MVC最大的不同是異步的、事件驅動的、非阻塞的,這使得應用程序的并發性能會大大提高,單位時間能夠處理更多的請求。這里不講WebFlux是怎么用的,有什么用,這類文章網上有太多了,而且都寫的非常不錯。下面主要看下WebFlux是怎么從無到有,框架怎么設計的,已期能夠更靈活的使用WebFlux。
Spring最牛逼的地方就是,無論啥東西,都可以無縫的集成到Spring。這得益于Spring體系優良的抽象封裝能力。WebFlux框架也一樣,底層實現其實不是Spring的,它依賴reactor和netty等。Spring做的就是通過抽象和封裝,把reactor的能力通過你最熟悉不過的Controller來使用。而且不局限于此,除了支持和Spring Mvc一樣的控制器編碼模式,還支持路由器模式(RouterFunctions),還支持端點模式(EndPoint)等。WebFlux所有功能其實內部只由幾個抽象類構建而成:
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見名之意,就是Reacive服務器的抽象類,它定義了服務的基本方法行為,包含啟動,停止等接口。結構如下:
public interface WebServer { void start() throws WebServerException; void stop() throws WebServerException; int getPort(); }
Spring默認有五個WebServer的實現,默認的不特別指定情況下,在spring-boot-starter-webflux自帶的是Netty的實現,其實現類如下:
對應WebServer,每個實現都會有一個工廠類對應,主要準備創建WebServer實例的資源,如NettyReactiveWebServerFactory生產WebServer方法:
public WebServer getWebServer(HttpHandler httpHandler) { HttpServer httpServer = createHttpServer(); ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter( httpHandler); return new NettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout); }
可以看到,在創建WebServer實例時,傳入了一個入參,HttpHandler。而且進而傳入了一個HttpHandlerAdapter實例里,這是因為每個WebServer的接收處理接口的適配器是不一樣的,在每個不同的WebServer工廠里通過不過的適配器去適配不同的實現。最后轉化成統一設計的HttpHandler里,見下面。
接下來看下HttpHandler,上面在創建WebServer的時候,傳了一個入參,類型就是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的啟動流程。
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反應式編程設計及工作原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。