首頁/ 汽車/ 正文

怎麼回答面試時被問WebFlux與WebMVC有什麼區別?

在構建響應式 Web 服務上,Spring 5 中引入了全新的程式設計框架,那就是 Spring WebFlux。作為一款新型的 Web 服務開發框架,它與傳統的 WebMVC 相比具體有哪些優勢呢?

Spring WebFlux 的應用場景

WebFlux 用於構建響應式 Web 服務。在詳細介紹 WebFlux 之前,我們先梳理一下這個新框架的應用場景,瞭解應用場景才能幫助我們對所要採用的技術體系做出正確的選擇。

微服務架構的興起為 WebFlux 的應用提供了一個很好的場景。我們知道在一個微服務系統中,存在數十乃至數百個獨立的微服務,它們相互通訊以完成複雜的業務流程。這個過程勢必會涉及大量的 I/O 操作,尤其是阻塞式 I/O 操作會整體增加系統的延遲並降低吞吐量。如果能夠在複雜的流程中整合非阻塞、非同步通訊機制,我們就可以高效處理跨服務之間的網路請求。針對這種場景,WebFlux 是一種非常有效的解決方案。

2022年了,微服務、雲原生、分庫分表要了解一下嗎?

從 WebMVC 到 WebFlux

接下來,我們將討論 WebMVC 與 WebFlux 之間的差別,而這些差別實際上正是體現在從 WebMVC 到 WebFlux 的演進過程中。讓我們先從傳統的 Spring WebMVC 技術棧開始說起。

Spring WebMVC技術棧

一般而言,Web 請求處理機制都會使用“管道-過濾器(Pipe-Filter)”架構模式,而 Spring WebMVC 作為一種處理 Web 請求的典型實現方案,同樣使用了 Servlet 中的過濾器鏈(FilterChain)來對請求進行攔截,如下圖所示。

怎麼回答面試時被問WebFlux與WebMVC有什麼區別?

我們知道 WebMVC 執行在 Servlet 容器上,這些容器常用的包括 Tomcat、JBoss 等。當 HTTP 請求透過 Servlet 容器時就會被轉換為一個 ServletRequest 物件,而最終返回一個 ServletResponse 物件,FilterChain 的定義如下所示。

public interface FilterChain { public void doFilter (ServletRequest request, ServletResponse response ) throws IOException, ServletException; }

當 ServletRequest 透過過濾器鏈中所包含的一系列過濾器之後,最終就會到達作為前端控制器的 DispatcherServlet。DispatcherServlet 是 WebMVC 的核心元件,擴充套件了 Servlet 物件,並持有一組 HandlerMapping 和 HandlerAdapter。

當 ServletRequest 請求到達時,DispatcherServlet 負責搜尋 HandlerMapping 例項並使用合適的 HandlerAdapter 對其進行適配。其中,HandlerMapping 的作用是根據當前請求找到對應的處理器 Handler,它只定義了一個方法,如下所示。

public interface HandlerMapping { //找到與請求對應的 Handler,封裝為一個 HandlerExecutionChain 返回 HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;}

而 HandlerAdapter 根據給定的 HttpServletRequest 和 HttpServletResponse 物件真正呼叫給定的 Handler,核心方法如下所示。

public interface HandlerAdapter { //針對給定的請求/響應物件呼叫目標 Handler ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;}

在執行過程中,DispatcherServlet 會在應用上下文中搜索所有 HandlerMapping。日常開發過程中,最常用的 HandlerMapping 包含 BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping,前者負責檢測所有 Controller 並根據請求 URL 的匹配規則對映到具體的 Controller 例項上,而後者基於 @RequestMapping 註解來找到目標 Controller。

如果我們使用了 RequestMappingHandlerMapping,那麼對應的 HandlerAdapter 就是 RequestMappingHandlerAdapter,它負責將傳入的 ServletRequest 繫結到添加了 @RequestMapping 註解的控制器方法上,從而實現對請求的正確響應。同時, HandlerAdapter 還提供請求驗證和響應轉換等輔助性功能,使得 Spring WebMVC 框架在日常 Web 開發中非常實用。

作為總結,我梳理了 Spring WebMVC 的整體架構,如下圖所示。

怎麼回答面試時被問WebFlux與WebMVC有什麼區別?

一直以來,Spring WebMVC 是我們開發 Web 服務的主流框架。但要注意的是,儘管 Servlet 本身在新版本中提供了非同步非阻塞的通訊機制,但 Spring WebMVC 在實現上並不允許在整個請求生命週期中都採用非阻塞式的操作方式。因此,Spring 在儘量沿用原有的開發模式以及 API 設計上提供了支援非同步非阻塞的 Spring WebFlux 框架。

Spring WebFlux 技術棧

介紹完 Spring WebMVC,我們來說說 Spring WebFlux。事實上,前面介紹的 HandlerMapping、HandlerAdapter 等元件在 WebFlux 裡都有同名的響應式版本,這是 WebFlux 的一種設計理念,即在既有設計的基礎上,提供新的實現版本,只對部分需要增強和弱化的地方做了調整。

我們先來看第一個需要調整的地方,顯然,我們應該替換掉原有的 Servlet API 以便融入響應式流。因此,在 WebFlux 中,代表請求和響應的是全新的 ServerHttpRequest 和 ServerHttpResponse 物件。

同樣,WebFlux 中同樣提供了一個過濾器鏈 WebFilterChain,定義如下。

public interface WebFilterChain { Mono filter(ServerWebExchange exchange);}

這裡的 ServerWebExchange 相當於一個上下文容器,儲存了 ServerHttpRequest、ServerHttpResponse 以及一些框架執行時狀態資訊。

在 WebFlux 中,和 WebMVC 中的 DispatcherServlet 相對應的元件是 DispatcherHandler。與 DispatcherServlet 類似,DispatcherHandler 同樣使用了一套響應式版本的 HandlerMapping 和 HandlerAdapter 完成對請求的處理。請注意,這兩個介面是定義在 org。springframework。web。reactive 包中,而不是在原有的 org。springframework。web 包中。響應式版本的 HandlerMapping 介面定義如下,可以看到這裡返回的是一個 Mono 物件,從而啟用了響應式行為模式。

public interface HandlerMapping { Mono getHandler(ServerWebExchange exchange);}

同樣,我們找到響應式版本的 HandlerAdapter,如下所示。

public interface HandlerAdapter { Mono handle(ServerWebExchange exchange, Object handler);}

對比非響應式版本的 HandlerAdapter,這裡的 ServerWebExchange 中同時包含了 ServerHttpRequest 和 ServerHttpResponse 物件,而 HandlerResult 則代表了處理結果。相比 WebMVC 中 ModelAndView 這種比較模糊的返回結果,HandlerResult 更加直接和明確。

在 WebFlux 中,同樣實現了響應式版本的 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter,因此我們仍然可以採用註解的方法來構建 Controller。另一方面,WebFlux 中還提供了 RouterFunctionMapping 和 HandlerFunctionAdapter 組合,專門用來提供基於函數語言程式設計的開發模式。這樣 Spring WebFlux 的整體架構圖就演變成這樣。

怎麼回答面試時被問WebFlux與WebMVC有什麼區別?

請注意,在處理 HTTP 請求上,我們需要使用支援非同步非阻塞的響應式伺服器引擎,常見的包括 Netty、Undertow 以及支援 Servlet 3。1 及以上版本的 Servlet 容器。

對比 WebFlux 和 WebMVC 的處理模型

現在我們已經明確了 WebMVC 到 WebFlux 的演進過程,但你可能會問,新的 WebFlux 要比傳統 WebMVC 好在哪裡呢?從兩者的處理模型上入手可以幫助你很好地理解這個問題,我們一起來看一下。

WebFlux 和 Web MVC 中的處理模型

透過前面的討論你已經知道 Servlet 是阻塞式的,所以 WebMVC 建立在阻塞 I/O 之上,我們來分析這種模型下執行緒處理請求的過程。假設有一個工作執行緒會處理來自客戶端的請求,所有請求構成一個請求佇列,並由一個執行緒按順序進行處理。針對一個請求,執行緒需要執行兩部分工作,首先是接受請求,然後再對其進行處理,如下圖所示。

怎麼回答面試時被問WebFlux與WebMVC有什麼區別?

在前面的示例中,正如你可能注意到的,工作執行緒的實際處理時間遠小於花費在阻塞操作上的時間。這意味著工作執行緒會被 I/O 讀取或寫入資料這一操作所阻塞。從這個簡單的圖中,

我們可以得出結論,執行緒效率低下

。同時,因為所有請求是排隊的,相當於一個請求佇列,所以接受請求和處理請求這兩部分操作實際上是可以共享等待時間的。

相比之下,WebFlux 構建在非阻塞 API 之上,這意味著沒有操作需要與 I/O 阻塞執行緒進行互動。接受和處理請求的效率很高,如下圖所示。

怎麼回答面試時被問WebFlux與WebMVC有什麼區別?

將上圖中所展示的非同步非阻塞請求處理與前面的阻塞過程進行比較,我們會注意到,現在沒有在讀取請求資料時發生等待,工作執行緒高效接受新連線。然後,提供了非阻塞 I/O 機制的底層作業系統會告訴我們請求資料是否已經接收完成,並且處理器可以在不阻塞的情況下進行處理。

類似的,寫入響應結果時同樣不需要阻塞,作業系統會在準備好將一部分資料非阻塞地寫入 I/O 時通知我們。這樣,我們就擁有了最佳的 CPU 利用率。

前面的示例展示了 WebFlux 比 WebMVC 更有效地利用一個工作執行緒,因此可以在相同的時間內處理更多的請求。那麼,如果是在多執行緒的場景下會發生什麼呢?我們來看下面這張圖。

怎麼回答面試時被問WebFlux與WebMVC有什麼區別?

從上圖中可以看出,多執行緒模型允許更快地處理排隊請求,能夠同時接受、處理和響應幾乎相同數量的請求。當然,我們明白多執行緒技術有利有弊。當處理使用者請求涉及太多的執行緒例項時,相互之間就需要協調資源,這是由於它們之間的不一致性會導致效能下降。

處理模型對效能的影響

講到這裡,你可能會問,不同的處理模型對效能會有多大程度的影響呢?這裡我們就引用維護Spring 框架的 Pivotal 公司軟體開發主管 Biju Kunjummen 的測試結果來對這一問題進行解答。

在 Biju Kunjummen 的測試用例中,他分別基於 WebMVC 所提供的阻塞式 RestTemplate 以及 WebFlux 所提供的非阻塞式 WebClient 工具類對遠端 Web 服務發起請求。對於不同組的併發使用者(300、1000、1500、3000、5000),他分別傳送了一個 delay 屬性設定為 300 ms 的請求,每個使用者重複該場景 30 次,請求之間的延遲為 1 到 2 秒。測試用例中使用了 Gatling 這款工具來執行壓測。

這裡我們擷取 300 和 3000 併發使用者場景下的結果進行對比,如下面兩張圖所示。

怎麼回答面試時被問WebFlux與WebMVC有什麼區別?

怎麼回答面試時被問WebFlux與WebMVC有什麼區別?

可以看到,在 300 併發使用者的測試用例下,WebMVC 和 WebFlux 的表現比較接近,意味著在併發量不高的情況下,非阻塞式的請求處理過程並沒有太多優勢;而在 3000 併發使用者下,情況就完全不一樣了。無論是吞吐量還是響應時間,WebFlux 都具有壓倒性的效能優勢。(完整版的測試結果和資料,你可以參考 Biju Kunjummen 的這篇文章進行獲取:https://dzone。com/articles/raw-performance-numbers-spring-boot-2-webflux-vs-s)

Original reprint:https://mp。weixin。qq。com/s/j-XeMR8AUUMeapHXuqIZCw

相關文章

Copyright © 2024百聞網
頂部