這幾年隨著微服務(wù)編程的興起,當(dāng)我們?cè)跇?gòu)建一個(gè)應(yīng)用時(shí)通常不會(huì)采用傳統(tǒng)的單體模式(所謂的單體模式岖妄,就是將所有功能包含在一個(gè)大而全的工程中),而是用許多只負(fù)責(zé)某一特定功能的微服務(wù)來實(shí)現(xiàn)寂祥。這些劃分為一個(gè)個(gè)的微服務(wù)通常只做一件事情荐虐,也通常能夠?qū)⑦@個(gè)功能做的很好。
但是丸凭,當(dāng)我們采用微服務(wù)架構(gòu)時(shí)福扬,首先要考慮的就是如何將所要構(gòu)建的應(yīng)用拆分成粒度大小合適、功能相對(duì)獨(dú)立的一個(gè)個(gè)微服務(wù)惜犀,而且也需要考慮這些微服務(wù)之間如何進(jìn)行通信(也就是彼此互相調(diào)用)铛碑。此時(shí),這些微服務(wù)之間的交互就變成了重中之重虽界。不過也不用太擔(dān)心汽烦,現(xiàn)在業(yè)界已經(jīng)有了許多成熟的解決方案,有興趣的同學(xué)可以參考我之前一系列的關(guān)于Spring Cloud的文章莉御,這里就不擴(kuò)展說明了撇吞。
你可能會(huì)納悶,標(biāo)題不是說響應(yīng)式編程礁叔,為何一開始就大談特談微服務(wù)牍颈。不用擔(dān)心哈,接下來的及后續(xù)的一系列文章琅关,我們所講的重點(diǎn)也不是微服務(wù)煮岁!我們要講的是在構(gòu)建微服務(wù)時(shí)所隱藏的一個(gè)架構(gòu)問題。這個(gè)問題就是:一旦我們將應(yīng)用轉(zhuǎn)換為微服務(wù)架構(gòu)涣易,那么隨之而來就是要面對(duì)這個(gè)應(yīng)用將變成一個(gè)分布式系統(tǒng)人乓,不論你愿不愿意承認(rèn),它就在那里都毒。而我們當(dāng)然知道分布式系統(tǒng)的優(yōu)點(diǎn),但也深知它的一個(gè)致命缺點(diǎn):服務(wù)雪崩碰缔。
或許你之前已經(jīng)學(xué)習(xí)過Spring Cloud账劲。Spring Cloud中的Hystrix或Resilience4j不是一個(gè)現(xiàn)成的解決方案么?當(dāng)然它們是解決服務(wù)雪崩的一個(gè)利器,但是要記住它是站在整體架構(gòu)上的一個(gè)解決方案瀑焦,而當(dāng)我們深入到業(yè)務(wù)實(shí)現(xiàn)層面時(shí)不免會(huì)顯得過于粗曠(誰讓我們都是苦命的coder)腌且。那么有沒有其它更細(xì)顆粒的解決方案呢?
先不要著急給答案榛瓮。還是先讓我們?cè)俅位氐轿⒎?wù)架構(gòu)所要解決的幾個(gè)問題铺董,也可以說是目標(biāo):
- 各個(gè)微服務(wù)之間是一種松耦合式的合作關(guān)系。這個(gè)也是我們編程以來一直追求的目標(biāo)禀晓;
- 快速響應(yīng)用戶的請(qǐng)求精续。俗話說:天下功夫唯快不破;
- 系統(tǒng)具有良好的彈性粹懒,能夠應(yīng)對(duì)不同的壓力負(fù)荷重付。誰不想構(gòu)建一個(gè)微信級(jí)別的應(yīng)用呢;
- 達(dá)到4個(gè)9或5個(gè)9的可用性凫乖。
針對(duì)上面的這些問題确垫,有一波業(yè)界大神們提出了The Reactive Manifesto: 反應(yīng)式宣言(熟悉么?有同學(xué)記得敏捷宣言么帽芽?)删掀。該宣言是一份構(gòu)建現(xiàn)代云擴(kuò)展架構(gòu)的處方,可以妥妥的解決上面問題导街。在該宣言中給出的處方主要是使用消息驅(qū)動(dòng)的方式來構(gòu)建應(yīng)用披泪,在形式上可以達(dá)到彈性和韌性,最后可以產(chǎn)生響應(yīng)性價(jià)值菊匿。該架構(gòu)可以使用下圖來表示:
該宣言的核心是借助消息驅(qū)動(dòng)來完成異步付呕、松耦合、隔絕跌捆、地址透明徽职、將錯(cuò)誤作為消息和無阻塞式的編程范型。這種編程范型也就稱之為:響應(yīng)式編程佩厚。而將我們現(xiàn)在所經(jīng)常使用的編程范型則稱之為:命令式編程姆钉。
命令式編程是一種阻塞模式的編程,也就是代碼是按照所編寫的順序一行一行的執(zhí)行抄瓦。響應(yīng)式編程則是一種非阻塞的異步編程潮瓶,我們無需等待。此外钙姊,響應(yīng)式編程是一種數(shù)據(jù)流編程模式毯辅,程序所關(guān)注的是數(shù)據(jù)流而非控制流。
對(duì)反應(yīng)式宣言感興趣的同學(xué)可以從這里反應(yīng)式宣言前往煞额。
前面說了那么多廢話思恐,下面正式進(jìn)入主題沾谜。
1. Reactive Streams
Java在JDK9之后引入了響應(yīng)式編程范型:Reactive Streams。Reactive Streams提供了一套非阻塞具有背壓的異步流處理標(biāo)準(zhǔn)胀莹。主要應(yīng)用在JVM基跑、JavaScript及網(wǎng)絡(luò)協(xié)議的處理中。我們也可以這么理解: Reactive Streams定義了一套響應(yīng)式編程的標(biāo)準(zhǔn)描焰。
Reactive Streams定義了下面4個(gè)API接口:
Publisher<T>
: 即事件或數(shù)據(jù)的發(fā)生源媳否,它只有一個(gè)訂閱方法:subscribe()
;Subscriber<T>
: 事件或數(shù)據(jù)的訂閱者,也可以稱為事件或數(shù)據(jù)的消費(fèi)者荆秦。該接口提供了4個(gè)方法篱竭,分別用于處理Publisher
所發(fā)出的不同消息;-
Subscription
: 該接口用來描述每個(gè)訂閱消息萄凤,有點(diǎn)類似我們?cè)谶M(jìn)行Web編程時(shí)所使用的會(huì)話信息(session)室抽,只不過該接口用來處理訂閱者和發(fā)布者之間的關(guān)聯(lián)信息。該接口有2個(gè)方法:request()
和cancel()
:-
request()
方法則是向上游消息發(fā)布者(Publisher)索要指定個(gè)數(shù)的消息靡努; -
cancel()
則是告訴消息發(fā)布者取消消息的推送坪圾。注意,一旦我們調(diào)用了該方法惑朦,則Subscriber之后不再接受任何Publisher所發(fā)布的消息兽泄;
-
Processor<T, R>
: 該接口同時(shí)繼承了Publisher
和Subscriber
這兩個(gè)接口。它即是一個(gè)消息發(fā)生者漾月,也是一個(gè)消息的訂閱者病梢,一般可用對(duì)所要發(fā)布的消息進(jìn)行轉(zhuǎn)換、合并等處理梁肿,可以理解為是發(fā)生者和訂閱者之間的一個(gè)處理管道蜓陌。
需要著重說明的一點(diǎn)是:雖然Reactive Streams是Java所提出的一個(gè)響應(yīng)式編程的標(biāo)準(zhǔn),但是并在JDK中提供任何具體的實(shí)現(xiàn)吩蔑,具體的實(shí)現(xiàn)則是由第三方提供钮热,如:RxJava、Reactor烛芬、akka...
2. 觀察者模式(Observer Pattern)
在我們進(jìn)入Reactive Streams正題之前隧期,讓我們先來回顧一個(gè)古老的Java編程模式:觀察者模式(Observer Pattern)。觀察者模式是GoF(Gang of Four)最早提出的23個(gè)著名模式中的一個(gè)(如果你還沒有聽說過赘娄,那么趕快去讀這本書《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》)仆潮。
我們?cè)陂_發(fā)一個(gè)軟件應(yīng)用時(shí),常常會(huì)有這樣需求:當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生變化的時(shí)候遣臼,某些其它的對(duì)象要做出相應(yīng)的響應(yīng)或改變性置。當(dāng)然我們可以使用n多種方法來做到這一點(diǎn),但是在具體開發(fā)時(shí)一方面要使所開發(fā)出的代碼能夠易于復(fù)用揍堰,另外一方面還要盡量選擇低耦合度的設(shè)計(jì)方案蚌讼。此時(shí)觀察者模式就是我們最好的選擇辟灰。
觀察者模式是指當(dāng)對(duì)象之間如果存在一對(duì)多的依賴關(guān)系時(shí),我們可讓依賴者們同時(shí)監(jiān)聽主題對(duì)象篡石。這樣當(dāng)這個(gè)主題對(duì)象在狀態(tài)上發(fā)生變化時(shí),就會(huì)通知所有依賴者西采,也就是觀察者對(duì)象凰萨,使它們能夠自動(dòng)做出響應(yīng)。觀察者模式屬于對(duì)象的行為型模式之一械馆。有時(shí)我們也將觀察者模式稱為發(fā)布-訂閱(Publish/Subscribe)模式胖眷、模型-視圖(Model/View)模式或從屬者(Dependents)模式。
或許霹崎,你會(huì)非常奇怪珊搀,我們不是在講響應(yīng)式編程么,這個(gè)和觀察者模式有什么關(guān)系尾菇。不要著急境析,相信我,這個(gè)模式和我們所要講的響應(yīng)式編程還真的有很大很大的關(guān)系派诬。
在一個(gè)典型的觀察者模式實(shí)現(xiàn)中包含了以下兩個(gè)接口:Subject
和Observer
劳淆。一種可能的代碼如下:
public interface Subject<T> {
void attach(Observer<T> observer);
void detach(Observer<T> observer);
void notify(T event);
}
public interface Observer<T> {
void update(T event);
}
這里Subject
是主題,也就是會(huì)引發(fā)變化的對(duì)象默赂,而Observer
就是觀察者沛鸵,通常也稱之為監(jiān)聽者,監(jiān)聽Subject
所產(chǎn)生的事件并進(jìn)行響應(yīng)缆八。
不過Observer
可能負(fù)責(zé)向Subject
注冊(cè)曲掰,也有可能不負(fù)責(zé)向Subject
注冊(cè)。Subject
則會(huì)保存所有注冊(cè)的Observer奈辰,
并在狀態(tài)發(fā)生變化時(shí)通知給所有的Observer
栏妖。而Subject
和Observer
之間并不需要對(duì)彼此知道太多,我們可以用下圖來表示它們之間的關(guān)系:
觀察者模式最常用的一個(gè)場(chǎng)景就是我們所熟知的MVC(Model-View-Controller)開發(fā)模式冯挎。此外底哥,做過UI層面開發(fā)的也知道,用戶操作事件處理也是一個(gè)典型的觀察者模式應(yīng)用房官。
非常幸運(yùn)趾徽,當(dāng)我們?cè)陂_發(fā)中需要使用觀察者模式,不需要從頭一點(diǎn)點(diǎn)實(shí)現(xiàn)翰守。Spring框架為我們提供了方便的腳手架(工具類):@EventListener
和ApplicationEventPublisher
孵奶。
-
@EventListener
用來注解哪些需要監(jiān)聽并處理相應(yīng)業(yè)務(wù)的組件或類; -
ApplicationEventPublisher
則是可以讓我們發(fā)布所定義的各種事件蜡峰。
不過需要澄清的一點(diǎn)了袁,Spring所提供的事件機(jī)制并不完全是一個(gè)標(biāo)準(zhǔn)觀察者模式的實(shí)現(xiàn)朗恳,而是在其上的一個(gè)變體:發(fā)布-訂閱模式(Publish-Subscribe)。相應(yīng)的载绿,在Spring中將Subject
和Observer
這兩個(gè)概念分別轉(zhuǎn)化為Publisher
和Subscriber
粥诫,并進(jìn)一步通過所提供的EventChannel
進(jìn)行兩者之間的解耦。該模式我們可以用下圖來表示:
該模式使得訂閱者(Subscriber
)只需要關(guān)注EventChannel
崭庸,而無需知道一個(gè)消息具體是哪一個(gè)發(fā)布者所發(fā)布怀浆。同時(shí)對(duì)于發(fā)布者來說,其可以將自己所發(fā)布的消息發(fā)布到同一個(gè)EventChannel
中怕享,也無須關(guān)心到底是哪一個(gè)訂閱者進(jìn)行了消費(fèi)执赡。
而這個(gè)EventChannel
也就是我們所熟知的消息代理(Message broker),如:kafka函筋、RabbitMQ或者Spring Cloud中的Event Bus沙合。同時(shí)借助EventChannel
我們還可以增加一些消息過濾器,使用這些過濾器就可以對(duì)消息進(jìn)行一些通用的處理跌帐。因此首懈,通過發(fā)布-訂閱模式我們可以非常容易借助現(xiàn)有的消息中間件完成各微服務(wù)之間的協(xié)作,并且解耦含末。
可見通過觀察者模式我們可以實(shí)現(xiàn)多個(gè)對(duì)象之間猜拾、多個(gè)服務(wù)之間關(guān)系的解耦,使得它們之間無須知道彼此的具體實(shí)現(xiàn)細(xì)節(jié)佣盒,甚至也不需要相互依賴挎袜。耦合的減少利于系統(tǒng)的復(fù)用,同時(shí)觀察者模式使得這些低耦合度的對(duì)象肥惭、服務(wù)之間能夠維持行動(dòng)的協(xié)調(diào)一致盯仪,保證高度的協(xié)作(Collaboration),為構(gòu)建分布式應(yīng)用奠定了基礎(chǔ)蜜葱∪埃可以這么說,響應(yīng)式編程就是建筑在這種模式之上的更為靈活的異步非阻塞的編程模式牵囤。
到這里爸黄,我想你應(yīng)該明白之前為什么說觀察者模式(Observer Pattern)和我們接下來所要講的響應(yīng)式編程之間的關(guān)系了吧(如果還是不明白也不用擔(dān)心,隨著后面的講解我想你一定會(huì)明白的)揭鳞。
好吧炕贵,下一篇讓我們正式開啟響應(yīng)式編程之旅。