應(yīng)用響應(yīng)式Web開(kāi)發(fā)組件-響應(yīng)式編程和Spring Boot
當(dāng)下,對(duì)于具有廣大用戶(hù)群體的新型互聯(lián)網(wǎng)應(yīng)用而言,它們基本都需要 考慮如何高效應(yīng)對(duì)用戶(hù)流量、如何確保系統(tǒng)彈性等核心技術(shù)主題。在理論和 實(shí)踐的結(jié)合下,響應(yīng)式編程是一種新型的編程模型,是確保系統(tǒng)彈性的一款 強(qiáng)有力的武器。在響應(yīng)式編程領(lǐng)域,存在一套完整的響應(yīng)式流規(guī)范以及實(shí)現(xiàn) 這一規(guī)范的開(kāi)發(fā)工具。在現(xiàn)實(shí)中,開(kāi)發(fā)人員通常不會(huì)直接使用這些偏底層的 開(kāi)發(fā)工具來(lái)開(kāi)發(fā)應(yīng)用程序,而是借助于特定的開(kāi)發(fā)框架。而我們?nèi)粘i_(kāi)發(fā)中 每天都在使用的Spring就是這樣一個(gè)支持響應(yīng)式編程的開(kāi)發(fā)框架。
在2017年,Spring發(fā)布了新版本Spring 5,這是自Spring 4發(fā)布以來(lái)將 近4年的時(shí)間中所發(fā)布的第一個(gè)全新版本。Spring 5引入了很多核心功能,重 要的是它全面擁抱了響應(yīng)式編程的設(shè)計(jì)思想和實(shí)踐。
Spring 5的響應(yīng)式編程模型以Project Reactor庫(kù)為基礎(chǔ),而后者則實(shí)現(xiàn) 了響應(yīng)式流規(guī)范。事實(shí)上,Spring Boot從2.x版本開(kāi)始全面依賴(lài)Spring 5。
Spring Boot為我們提供了一系列響應(yīng)式編程組件,而本章將重點(diǎn)關(guān)注如何使 用Spring Boot框架來(lái)開(kāi)發(fā)響應(yīng)式Web服務(wù)。
響應(yīng)式編程和Spring Boot
響應(yīng)式編程是一種新的編程技術(shù),其目的是構(gòu)建響應(yīng)式系統(tǒng)。對(duì)于響應(yīng) 式系統(tǒng)而言,任何時(shí)候都需要確保其具備即時(shí)響應(yīng)性,這是大多數(shù)日常業(yè)務(wù) 場(chǎng)景所需要的,但卻是一項(xiàng)非常復(fù)雜而有挑戰(zhàn)性的任務(wù),需要對(duì)相關(guān)技術(shù)有 深入的了解。本節(jié)將討論這些技術(shù)。
響應(yīng)式流規(guī)范和實(shí)現(xiàn)框架
對(duì)于響應(yīng)式編程而言,首先要明確的概念是數(shù)據(jù)流(Data Stream)。簡(jiǎn) 單來(lái)講,所謂的流就是由生產(chǎn)者生產(chǎn)并由一個(gè)或多個(gè)消費(fèi)者消費(fèi)的元素序 列。而一旦有了數(shù)據(jù)流,那么就勢(shì)必面臨流量控制問(wèn)題。流量控制是討論數(shù) 據(jù)流的核心話(huà)題。而針對(duì)如何控制流量,業(yè)界存在一個(gè)響應(yīng)式流規(guī)范,以及 一批實(shí)現(xiàn)了該規(guī)范的開(kāi)發(fā)工具。
1. 響應(yīng)式流規(guī)范
Java API版本的響應(yīng)式流只包含四個(gè)接口,即Publisher<T>、 Subscriber<T>、Subscription和Processor<T,R>。
發(fā)布者(Publisher)是潛在的包含無(wú)限數(shù)量的有序元素的生產(chǎn)者,它根 據(jù)收到的請(qǐng)求向當(dāng)前訂閱者發(fā)送元素。Publisher<T>接口定義如代碼清單5-1 所示。
代碼清單5-1 Publisher<T>接口定義代碼
public interface Publisher<T> {
public void subscribe(Subscriber<? super T> s);
}
訂閱者(Subscriber)從發(fā)布者那里訂閱并接收元素。發(fā)布者向訂閱者 發(fā)送訂閱令牌(Subscription Token)。通過(guò)訂閱令牌,訂閱者就可以向發(fā) 布者請(qǐng)求多個(gè)元素。當(dāng)元素準(zhǔn)備就緒時(shí),發(fā)布者就會(huì)向訂閱者發(fā)送合適數(shù)量 的元素。然后訂閱者可以請(qǐng)求更多的元素,發(fā)布者也可能有多個(gè)來(lái)自訂閱者 的待處理請(qǐng)求。Subscriber <T>接口定義如代碼清單5-2所示。

當(dāng)執(zhí)行發(fā)布者的subscribe()方法時(shí),發(fā)布者會(huì)回調(diào)訂閱者的 onSubscribe()方法。在這個(gè)方法中,通常訂閱者會(huì)借助傳入的Subscription 對(duì)象向發(fā)布者請(qǐng)求n個(gè)數(shù)據(jù)。然后發(fā)布者通過(guò)不斷調(diào)用訂閱者的onNext()方法 向訂閱者發(fā)出最多n個(gè)數(shù)據(jù)。如果數(shù)據(jù)全部發(fā)完,則會(huì)調(diào)用onComplete()方法 告知訂閱者流已經(jīng)發(fā)完;如果有錯(cuò)誤發(fā)生,則通過(guò)onError()方法發(fā)出錯(cuò)誤提 示消息,這時(shí)同樣也會(huì)終止數(shù)據(jù)流。
訂閱(Subscription)表示訂閱者訂閱的一個(gè)令牌。當(dāng)訂閱請(qǐng)求成功 時(shí),發(fā)布者將其傳遞給訂閱者。訂閱者使用訂閱令牌與發(fā)布者進(jìn)行交互,例 如請(qǐng)求更多的元素或取消訂閱。Subscription接口定義如代碼清單5-3所示。

當(dāng)發(fā)布者調(diào)用subscribe()方法注冊(cè)訂閱者時(shí),會(huì)通過(guò)訂閱者的回調(diào)方法 onSubscribe()傳入Subscription對(duì)象,之后訂閱者就可以使用這個(gè) Subscription對(duì)象的request()方法向發(fā)布者請(qǐng)求數(shù)據(jù)。
處理器(Processor)充當(dāng)訂閱者和發(fā)布者之間的轉(zhuǎn)換器 (Transformer)。Processor<T,R>訂閱類(lèi)型T的數(shù)據(jù)元素,接收并轉(zhuǎn)換為類(lèi) 型R的數(shù)據(jù),發(fā)布該數(shù)據(jù)。Processor接口同時(shí)繼承了Publisher和Subscriber 接口,其定義如代碼清單5-4所示。

上述四個(gè)接口是各個(gè)響應(yīng)式開(kāi)發(fā)庫(kù)之間互相實(shí)現(xiàn)兼容的橋梁,響應(yīng)式流 規(guī)范也僅僅聚焦于此,而對(duì)諸如轉(zhuǎn)換、合并、分組等的操作一概未做要求, 因此是一個(gè)非常抽象且精簡(jiǎn)的接口規(guī)范。
作為總結(jié),我們可以把響應(yīng)式流規(guī)范核心接口的交互方式梳理成圖5-1。

可以看到,圖5-1中所示的交互方式一共包含如下7個(gè)步驟。
1)當(dāng)發(fā)布者使用subscribe()方法實(shí)現(xiàn)對(duì)該發(fā)布者的訂閱時(shí),首先會(huì)創(chuàng) 建一個(gè)具有相應(yīng)邏輯的Subscription對(duì)象,這個(gè)Subscription對(duì)象定義了如 何處理請(qǐng)求,以及如何發(fā)出數(shù)據(jù)。
2)然后發(fā)布者將這個(gè)Subscription通過(guò)訂閱者的onSubscribe()方法傳 給訂閱者。
3)在訂閱者的onSubscribe()方法中,需要通過(guò)Subscription的request ()方法發(fā)起第一次請(qǐng)求。
4)Subscription收到請(qǐng)求,就可以通過(guò)回調(diào)訂閱者的onNext()方法發(fā)出 元素,有多少發(fā)多少,但不能超過(guò)請(qǐng)求的個(gè)數(shù)。
5)訂閱者在onNext()方法中通常定義對(duì)元素的處理邏輯,處理完成之 后,可以繼續(xù)發(fā)起請(qǐng)求。
6)發(fā)布者根據(jù)需要繼續(xù)滿(mǎn)足訂閱者的請(qǐng)求。
7)如果發(fā)布者的元素序列正常結(jié)束,就通過(guò)訂閱者的onComplete()方法 予以告知。如果序列發(fā)送過(guò)程中有錯(cuò)誤,則通過(guò)訂閱者的onError()方法予以 告知并傳遞錯(cuò)誤提示。這兩種情況都會(huì)導(dǎo)致序列終止,訂閱過(guò)程結(jié)束。
2. Project Reactor
Spring 5引入了響應(yīng)式編程機(jī)制,并默認(rèn)集成了Project Reactor(下文 簡(jiǎn)稱(chēng)為Reactor)作為該機(jī)制的實(shí)現(xiàn)框架。Reactor誕生較晚,可以認(rèn)為它是 第二代響應(yīng)式開(kāi)發(fā)框架。所以它是一款完全基于響應(yīng)式流規(guī)范設(shè)計(jì)和實(shí)現(xiàn)的 工具庫(kù),在使用上直觀易懂。
在Reactor框架中,數(shù)據(jù)流的表現(xiàn)形式如圖5-2所示。

圖5-2中的數(shù)據(jù)流模型從語(yǔ)義上可以用如下公式表示:
onNext x 0..N [onError | onComplete]
上述公式包含了如下三種不同類(lèi)型的方法調(diào)用,分別處理不同場(chǎng)景下的 消息通知。
onNext():正常包含元素的消息通知。
onComplete():序列結(jié)束的消息通知。
onError():序列出錯(cuò)的消息通知,可以沒(méi)有。
按照響應(yīng)式流規(guī)范,當(dāng)這些消息通知產(chǎn)生時(shí),訂閱者中對(duì)應(yīng)的 onNext()、onComplete()和onError()這三個(gè)方法將被調(diào)用。如果序列沒(méi)有出 錯(cuò),則onError()方法不會(huì)被調(diào)用;而如果不調(diào)用onComplete()方法,我們就 會(huì)得到一個(gè)無(wú)限異步序列。通常,無(wú)限異步序列應(yīng)該只用于測(cè)試等特殊場(chǎng) 景。
針對(duì)數(shù)據(jù)流,Reactor提供了兩個(gè)核心組件,即Flux和Mono。其中Flux代 表包含0到n個(gè)元素的異步序列,而Mono則表示包含0個(gè)或1個(gè)元素的異步序 列。
創(chuàng)建Flux的方式非常多,這些方式可以分成兩大類(lèi),一類(lèi)是充分利用 Flux的靜態(tài)方法,另一類(lèi)則是動(dòng)態(tài)創(chuàng)建Flux。這里的靜態(tài)方法常見(jiàn)的包括 just()、fromArray()、fromIterable()、fromStream()、empty()、 error()、never()、range()、interval()等,而動(dòng)態(tài)方法則包括generate() 和create()。創(chuàng)建Mono的方式也類(lèi)似。 另外,和其他主流的響應(yīng)式編程框架一樣,Reactor框架的設(shè)計(jì)目標(biāo)也是 為了簡(jiǎn)化響應(yīng)式流的使用方法。為此,Reactor框架為我們提供了大量操作符 用于操作Flux和Mono對(duì)象。常見(jiàn)的包括用于數(shù)據(jù)轉(zhuǎn)換的flatMap、用于數(shù)據(jù)過(guò) 濾的filter、用于操作組合的zipWith、用于條件控制的defaultIfEmpty,以 及subscribe和log等工具操作符。
由于本章的重點(diǎn)是介紹Spring WebFlux,而Project Reactor是WebFlux 的底層框架,我們一般不會(huì)直接使用該框架開(kāi)發(fā)Web應(yīng)用程序。因此,針對(duì) Flux和Mono的創(chuàng)建方法以及各種操作符,我們不打算做全面而細(xì)致的介紹, 讀者可參考Reactor框架的官方文檔以及筆者翻譯的《Spring響應(yīng)式編程》一 書(shū)做進(jìn)一步了解。
本文就是愿天堂沒(méi)有BUG給大家分享的內(nèi)容,大家有收獲的話(huà)可以分享下,想學(xué)習(xí)更多的話(huà)可以到微信公眾號(hào)里找我,我等你哦。
