Spring WebFlux(譯)

前言

今天在做冪等性校驗(yàn)由Spring WebMvc 轉(zhuǎn) Spring webFlux,發(fā)現(xiàn)網(wǎng)上關(guān)于 Spring webFlux 中文資料比較少,所以自己手動(dòng)有道翻譯一份,僅做自己學(xué)習(xí)使用,做個(gè)記錄,感謝!

關(guān)于 Spring WebFlux 的翻譯時(shí)基于版本 Version 5.3.6 的;

原文地址

1.Spring WebFlux

Spring框架中包含的原始web框架,Spring web MVC,是專門為Servlet API和Servlet容器而構(gòu)建的。響應(yīng)式堆棧web框架Spring WebFlux是在5.0版本中添加的。它是完全無(wú)阻塞的,支持響應(yīng)流回壓,并運(yùn)行在Netty、Undertow和Servlet 3.1+容器等服務(wù)器上

這兩個(gè)web框架都反映了它們的源模塊的名稱(Spring -webmvc和Spring -webflux),并在Spring框架中共存。每個(gè)模塊都是可選的。應(yīng)用程序可以使用一個(gè)或另一個(gè)模塊,或者在某些情況下同時(shí)使用兩個(gè)模塊——例如,帶有響應(yīng)性WebClient的Spring MVC控制器。

1.1. Overview (概述)

為什么要?jiǎng)?chuàng)建Spring WebFlux ?

部分原因是需要一個(gè)非阻塞的web堆棧來(lái)處理少量線程的并發(fā)性,并使用更少的硬件資源進(jìn)行擴(kuò)展。Servlet 3.1確實(shí)為非阻塞I/O提供了一個(gè)API。然而,使用它會(huì)遠(yuǎn)離Servlet API的其他部分,其中契約是同步的(Filter, Servlet)或阻塞的(getParameter, getPart)。這是一個(gè)新的通用API作為任何非阻塞運(yùn)行時(shí)的基礎(chǔ)的動(dòng)機(jī)。這很重要,因?yàn)榉?wù)器(如Netty)是在異步、非阻塞空間中建立的。

答案的另一部分是函數(shù)式編程。就像Java 5中添加注釋創(chuàng)造了機(jī)會(huì)(比如帶注釋的REST控制器或單元測(cè)試)一樣,Java 8中添加lambda表達(dá)式也為Java中的功能性api創(chuàng)造了機(jī)會(huì)。這對(duì)于允許聲明式組合異步邏輯的非阻塞應(yīng)用程序和延續(xù)風(fēng)格的api(由CompletableFuture和ReactiveX普及)來(lái)說(shuō)是一個(gè)福音。在編程模型級(jí)別,Java 8使Spring WebFlux能夠提供功能性的web端點(diǎn)和帶注釋的控制器。

1.1.1. Define “Reactive” (響應(yīng)式的定義)

我們談到了“非阻塞”和“功能性”,但是響應(yīng)式是什么意思呢?

術(shù)語(yǔ)“響應(yīng)式”指的是圍繞對(duì)變化作出反應(yīng)而構(gòu)建的編程模型——網(wǎng)絡(luò)組件對(duì)I/O事件作出反應(yīng),UI控制器對(duì)鼠標(biāo)事件作出反應(yīng),等等。從這個(gè)意義上說(shuō),非阻塞是響應(yīng)式的,因?yàn)槲覀儸F(xiàn)在處于操作完成或數(shù)據(jù)可用時(shí)響應(yīng)通知的模式,而不是被阻塞;

還有另一個(gè)重要的機(jī)制,我們?cè)赟pring團(tuán)隊(duì)中與“響應(yīng)式”聯(lián)系在一起,那就是無(wú)阻塞的背壓。在同步的命令式代碼中,阻塞調(diào)用是迫使調(diào)用者等待的一種自然形式的背壓。在非阻塞代碼中,控制事件的速率使快速的生產(chǎn)者不會(huì)淹沒(méi)其目標(biāo)變得很重要。

響應(yīng)流是一個(gè)小規(guī)范(也在Java 9中采用),它定義了帶有背壓的異步組件之間的交互。例如,數(shù)據(jù)存儲(chǔ)庫(kù)(充當(dāng)發(fā)布服務(wù)器)可以生成HTTP服務(wù)器(充當(dāng)訂閱服務(wù)器)隨后可以寫入響應(yīng)的數(shù)據(jù)。響應(yīng)流的主要目的是讓訂閱者控制發(fā)布者產(chǎn)生數(shù)據(jù)的速度。

思考:如果生產(chǎn)者不能放慢速度怎么辦?
響應(yīng)式流的目的只是建立機(jī)制和邊界。如果生產(chǎn)者不能放慢速度,它就必須決定是緩沖、刪除還是失敗。

1.1.2. Reactive API(響應(yīng)式API)

響應(yīng)式流在互操作性中扮演著重要的角色。它對(duì)庫(kù)和基礎(chǔ)設(shè)施組件很感興趣,但作為應(yīng)用程序API用處不大,因?yàn)樗图?jí)了。應(yīng)用程序需要一個(gè)更高級(jí)、更豐富的功能性API來(lái)組成異步邏輯——類似于Java 8 Stream API,但不只是用于集合。這就是響應(yīng)式庫(kù)所扮演的角色。

Reactor是Spring WebFlux的響應(yīng)式庫(kù)的選擇。它提供Mono和Flux API類型來(lái)處理0..1的數(shù)據(jù)序列(Mono)和0 . .N (Flux)到一組與操作符的ReactiveX詞匯表對(duì)齊的豐富操作符。Reactor是一個(gè)Reactive Streams庫(kù),因此,它的所有操作人員都支持無(wú)阻塞的背壓。Reactor非常關(guān)注服務(wù)器端Java。它是與Spring緊密協(xié)作開(kāi)發(fā)的。

WebFlux需要Reactor作為一個(gè)核心依賴,但是它可以通過(guò) reactive Streams 與其他的 reactive 庫(kù)互操作。作為一個(gè)通用規(guī)則,WebFlux API 接受一個(gè)普通的 Publisher 作為輸入,在內(nèi)部將其調(diào)整為一個(gè) Reactor 類型,使用它,并返回一個(gè)Flux或Mono作為輸出。因此,您可以傳遞任何 Publisher 作為輸入,并可以對(duì)輸出應(yīng)用操作,但您需要調(diào)整輸出以便與另一個(gè)響應(yīng)式庫(kù)一起使用。只要可行(例如,帶注釋的控制器),WebFlux就會(huì)透明地適應(yīng)RxJava或其他響應(yīng)性庫(kù)的使用。有關(guān)更多細(xì)節(jié),請(qǐng)參閱響應(yīng)式庫(kù)。

除了響應(yīng)式api之外,WebFlux還可以與Kotlin中的協(xié)程api一起使用,后者提供了一種更命定的編程風(fēng)格。下面的Kotlin代碼示例將與協(xié)程api一起提供。

1.1.3. Programming Models(編程模型)

Spring -web模塊包含了Spring WebFlux的響應(yīng)式基礎(chǔ),包括HTTP抽象、響應(yīng)式流適配器,支持的服務(wù)器、編解碼器和一個(gè)核心的WebHandler API,類似于Servlet API,但沒(méi)有阻塞協(xié)議。

在此基礎(chǔ)上,Spring WebFlux提供了兩種編程模型的選擇:

  • 帶注釋的控制器:與Spring MVC一致,并基于Spring-web模塊中的相同注釋。Spring MVC和WebFlux控制器都支持響應(yīng)式(Reactor和RxJava)返回類型,因此,很難將它們區(qū)分開(kāi)來(lái)。一個(gè)顯著的區(qū)別是,WebFlux也支持響應(yīng)式@RequestBody參數(shù)。

  • 函數(shù)式端點(diǎn):基于lambda的、輕量級(jí)的函數(shù)式編程模型。您可以將其視為應(yīng)用程序可以用來(lái)路由和處理請(qǐng)求的一個(gè)小庫(kù)或一組實(shí)用程序。帶注釋的控制器的最大區(qū)別在于,應(yīng)用程序從頭到尾負(fù)責(zé)請(qǐng)求處理,而不是通過(guò)注釋聲明意圖并被回調(diào)。

1.1.4. Applicability(使用范圍)

Spring MVC 還是 WebFlux?

這是一個(gè)很自然的問(wèn)題,但卻建立了一個(gè)不合理的二分法。實(shí)際上,兩者共同努力擴(kuò)大了可用選擇的范圍。這兩種設(shè)計(jì)都是為了彼此的連續(xù)性和一致性,它們可以并排使用,并且來(lái)自每一方的反饋對(duì)雙方都有利。下圖顯示了兩者之間的關(guān)系,它們有什么共同之處,以及它們各自支持的獨(dú)特之處:

image.png

我們建議您考慮以下幾點(diǎn):

  • 如果您有一個(gè)運(yùn)行良好的Spring MVC應(yīng)用程序,則不需要進(jìn)行更改。命令式編程是編寫、理解和調(diào)試代碼的最簡(jiǎn)單方法。您可以選擇最多的庫(kù),因?yàn)閺臍v史上看,大多數(shù)庫(kù)都是阻塞的。

  • 如果你已經(jīng)在購(gòu)買一個(gè)非阻塞的web堆棧,Spring WebFlux提供了與其他網(wǎng)站相同的執(zhí)行模型優(yōu)勢(shì),還提供了服務(wù)器(Netty, Tomcat, Jetty, Undertow和Servlet 3.1+容器),編程模型(帶注釋的控制器和功能web端點(diǎn)),并選擇響應(yīng)式庫(kù)(Reactor、RxJava或其他)。

  • 如果你對(duì)使用Java 8 lambdas或Kotlin的輕量級(jí)功能web框架感興趣,你可以使用Spring WebFlux功能web端點(diǎn)。對(duì)于需求不那么復(fù)雜的小型應(yīng)用程序或微服務(wù)來(lái)說(shuō),這也是一個(gè)很好的選擇,這些應(yīng)用程序或微服務(wù)可以受益于更大的透明度和控制。

  • 在微服務(wù)體系結(jié)構(gòu)中,你可以混合使用Spring MVC或Spring WebFlux控制器或Spring WebFlux功能端點(diǎn)的應(yīng)用。在兩個(gè)框架中支持相同的基于注釋的編程模型,可以更容易地重用知識(shí),同時(shí)為正確的工作選擇正確的工具。

  • 評(píng)估應(yīng)用程序的一個(gè)簡(jiǎn)單方法是檢查它的依賴關(guān)系。如果您需要使用阻塞持久性api (JPA、JDBC)或網(wǎng)絡(luò)api,那么Spring MVC至少是通用架構(gòu)的最佳選擇。在技術(shù)上,用Reactor和RxJava在單獨(dú)的線程上執(zhí)行阻塞調(diào)用是可行的,但你不會(huì)充分利用非阻塞的web堆棧。

  • 如果你有一個(gè)Spring MVC應(yīng)用程序調(diào)用遠(yuǎn)程服務(wù),試試響應(yīng)式WebClient。您可以直接從Spring MVC控制器方法返回響應(yīng)類型(Reactor、RxJava或其他)。每個(gè)調(diào)用的延遲時(shí)間或調(diào)用之間的相互依賴性越大,好處就越大。Spring MVC控制器也可以調(diào)用其他響應(yīng)性組件。

  • 如果您有一個(gè)大型的團(tuán)隊(duì),請(qǐng)記住,在向非阻塞、函數(shù)式和聲明式編程的轉(zhuǎn)變過(guò)程中,需要經(jīng)歷陡峭的學(xué)習(xí)曲線。一個(gè)不需要完全切換就啟動(dòng)的實(shí)用方法是使用響應(yīng)式WebClient。除此之外,從小事做起,衡量好處。我們預(yù)計(jì),對(duì)于廣泛的應(yīng)用來(lái)說(shuō),這種轉(zhuǎn)變是不必要的。如果您不確定要尋找什么好處,可以從學(xué)習(xí)非阻塞I/O如何工作(例如,單線程N(yùn)ode.js上的并發(fā)性)及其影響開(kāi)始。

1.1.5. Servers(服務(wù))

Spring WebFlux支持Tomcat, Jetty, Servlet 3.1+容器,以及Netty和Undertow等非Servlet運(yùn)行時(shí)。所有服務(wù)器都適應(yīng)于低級(jí)的公共API,這樣就可以跨服務(wù)器支持高級(jí)編程模型。

Spring WebFlux沒(méi)有啟動(dòng)或停止服務(wù)器的內(nèi)置支持。然而,很容易從Spring配置和WebFlux基礎(chǔ)設(shè)施組裝一個(gè)應(yīng)用程序,并使用幾行代碼運(yùn)行它。

Spring Boot有一個(gè)WebFlux啟動(dòng)器,可以自動(dòng)執(zhí)行這些步驟。默認(rèn)情況下,初學(xué)者使用Netty,但是通過(guò)改變Maven或Gradle的依賴關(guān)系,很容易切換到Tomcat、Jetty或Undertow。Spring Boot默認(rèn)為Netty,因?yàn)樗鼜V泛地用于異步、非阻塞空間,并允許客戶端和服務(wù)器共享資源。

Tomcat和Jetty可以與Spring MVC和WebFlux一起使用。但是請(qǐng)記住,它們的使用方式是非常不同的。Spring MVC依賴于Servlet阻塞I/O,并允許應(yīng)用程序在需要時(shí)直接使用Servlet API。Spring WebFlux依賴于Servlet 3.1非阻塞I/O,并使用底層適配器背后的Servlet API。不能直接使用。

對(duì)于Undertow, Spring WebFlux直接使用Undertow API,而不使用Servlet API。

1.1.6. Performance(性能)

性能具有許多特征和意義。響應(yīng)式和非阻塞通常不會(huì)使應(yīng)用程序運(yùn)行得更快。在某些情況下,它們可以(例如,如果使用WebClient并行運(yùn)行遠(yuǎn)程調(diào)用)??偟膩?lái)說(shuō),非阻塞方式需要更多的工作,這可能會(huì)略微增加所需的處理時(shí)間。

響應(yīng)式和非阻塞的主要預(yù)期好處是能夠使用少量固定數(shù)量的線程和更少的內(nèi)存進(jìn)行擴(kuò)展。這使得應(yīng)用程序在負(fù)載下更有彈性,因?yàn)樗鼈兛梢砸愿深A(yù)測(cè)的方式伸縮。然而,為了觀察這些好處,您需要有一些延遲(包括緩慢和不可預(yù)測(cè)的網(wǎng)絡(luò)I/O)。這就是反應(yīng)性堆棧開(kāi)始顯示其優(yōu)勢(shì)的地方,差異可能是戲劇性的。

1.1.7. Concurrency Model(并發(fā)模型)

Spring MVC和Spring WebFlux都支持帶注釋的控制器,但是在并發(fā)模型和阻塞和線程的默認(rèn)設(shè)置上有一個(gè)關(guān)鍵的區(qū)別。

在Spring MVC(以及一般的servlet應(yīng)用程序)中,設(shè)定應(yīng)用程序可以阻塞當(dāng)前線程(例如,對(duì)于遠(yuǎn)程調(diào)用)。因此,servlet容器使用大型線程池來(lái)吸收請(qǐng)求處理期間可能出現(xiàn)的阻塞。

在Spring WebFlux(以及一般的非阻塞服務(wù)器)中,設(shè)定應(yīng)用程序不會(huì)阻塞。因此,非阻塞服務(wù)器使用一個(gè)小的、固定大小的線程池(事件循環(huán)工作者)來(lái)處理請(qǐng)求。

“擴(kuò)展”和“少量線程”聽(tīng)起來(lái)可能矛盾,但永遠(yuǎn)不阻塞當(dāng)前線程(而依賴回調(diào))意味著你不需要額外的線程,因?yàn)闆](méi)有阻塞調(diào)用需要吸收。

調(diào)用阻塞API

如果您確實(shí)需要使用一個(gè)阻塞庫(kù)怎么辦?Reactor和RxJava都提供了publishOn操作符,以在不同的線程上繼續(xù)處理。這意味著有個(gè)容易的安全出口。但是,請(qǐng)記住,阻塞api并不適合這種并發(fā)模型。

可變狀態(tài)

在Reactor和RxJava中,通過(guò)操作符聲明邏輯。在運(yùn)行時(shí),會(huì)形成一個(gè)響應(yīng)式管道,其中數(shù)據(jù)會(huì)在不同的階段順序處理。這樣做的一個(gè)關(guān)鍵好處是,它使應(yīng)用程序不必保護(hù)可變狀態(tài),因?yàn)樵摴艿乐械膽?yīng)用程序代碼永遠(yuǎn)不會(huì)并發(fā)調(diào)用。

線程模型

您希望在使用Spring WebFlux運(yùn)行的服務(wù)器上看到哪些線程?

  • 在一個(gè) “普通的” Spring WebFlux服務(wù)器上(例如,沒(méi)有數(shù)據(jù)訪問(wèn)或其他可選依賴項(xiàng)),您可以期望一個(gè)線程用于服務(wù)器,其他幾個(gè)線程用于請(qǐng)求處理(通常和CPU內(nèi)核的數(shù)量一樣多)。然而,Servlet容器可能以更多的線程(例如,Tomcat上的10個(gè)線程)啟動(dòng),以支持Servlet(阻塞)I/O和Servlet 3.1(非阻塞)I/O的使用。

  • 響應(yīng)式WebClient以事件循環(huán)方式操作。因此,您可以看到與此相關(guān)的少量、固定數(shù)量的處理線程(例如,Reactor-http-nio-帶有Reactor Netty連接器)。但是,如果反應(yīng)器Netty同時(shí)用于客戶端和服務(wù)器,這兩個(gè)共享事件循環(huán)資源在默認(rèn)情況下。

  • Reactor和RxJava提供了線程池抽象,稱為調(diào)度程序,用于publishOn操作符,該操作符用于將處理切換到不同的線程池。調(diào)度器有建議特定并發(fā)策略的名稱——例如,“并行”(用于線程數(shù)量有限的cpu綁定工作)或“彈性”(用于I/ o綁定工作,用于線程數(shù)量很大)。如果您看到這樣的線程,這意味著某些代碼正在使用特定的線程池調(diào)度器策略。

  • 數(shù)據(jù)訪問(wèn)庫(kù)和其他第三方依賴項(xiàng)也可以創(chuàng)建和使用它們自己的線程。

配置

Spring框架不支持啟動(dòng)和停止服務(wù)器。要為服務(wù)器配置線程模型,您需要使用特定于服務(wù)器的配置api,或者,如果您使用Spring Boot,請(qǐng)檢查每個(gè)服務(wù)器的Spring Boot配置選項(xiàng)。您可以直接配置WebClient。對(duì)于所有其他庫(kù),請(qǐng)參閱它們各自的文檔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容