初入響應(yīng)式編程(上)

這幾年隨著微服務(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):

  1. 各個(gè)微服務(wù)之間是一種松耦合式的合作關(guān)系。這個(gè)也是我們編程以來一直追求的目標(biāo)禀晓;
  2. 快速響應(yīng)用戶的請(qǐng)求精续。俗話說:天下功夫唯快不破;
  3. 系統(tǒng)具有良好的彈性粹懒,能夠應(yīng)對(duì)不同的壓力負(fù)荷重付。誰不想構(gòu)建一個(gè)微信級(jí)別的應(yīng)用呢;
  4. 達(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)可以使用下圖來表示:

反應(yīng)式編程的架構(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接口:

  1. Publisher<T>: 即事件或數(shù)據(jù)的發(fā)生源媳否,它只有一個(gè)訂閱方法: subscribe();

  2. Subscriber<T>: 事件或數(shù)據(jù)的訂閱者,也可以稱為事件或數(shù)據(jù)的消費(fèi)者荆秦。該接口提供了4個(gè)方法篱竭,分別用于處理Publisher所發(fā)出的不同消息;

  3. 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ā)布的消息兽泄;
  4. Processor<T, R>: 該接口同時(shí)繼承了PublisherSubscriber這兩個(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è)接口:SubjectObserver劳淆。一種可能的代碼如下:

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栏妖。而SubjectObserver之間并不需要對(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框架為我們提供了方便的腳手架(工具類):@EventListenerApplicationEventPublisher孵奶。

  • @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中將SubjectObserver這兩個(gè)概念分別轉(zhuǎn)化為PublisherSubscriber粥诫,并進(jìn)一步通過所提供的EventChannel進(jìn)行兩者之間的解耦。該模式我們可以用下圖來表示:

Spring事件機(jī)制

該模式使得訂閱者(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)式編程之旅。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末野崇,一起剝皮案震驚了整個(gè)濱河市称开,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖鳖轰,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件清酥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蕴侣,警方通過查閱死者的電腦和手機(jī)焰轻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昆雀,“玉大人鹦马,你說我怎么就攤上這事∫渖觯” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵菱肖,是天一觀的道長(zhǎng)客冈。 經(jīng)常有香客問我,道長(zhǎng)稳强,這世上最難降的妖魔是什么场仲? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮退疫,結(jié)果婚禮上渠缕,老公的妹妹穿的比我還像新娘。我一直安慰自己褒繁,他們只是感情好亦鳞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棒坏,像睡著了一般燕差。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坝冕,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天徒探,我揣著相機(jī)與錄音,去河邊找鬼喂窟。 笑死测暗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的磨澡。 我是一名探鬼主播碗啄,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼钱贯!你這毒婦竟也來了挫掏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤秩命,失蹤者是張志新(化名)和其女友劉穎尉共,沒想到半個(gè)月后褒傅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袄友,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年殿托,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剧蚣。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡支竹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸠按,到底是詐尸還是另有隱情礼搁,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布目尖,位于F島的核電站馒吴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瑟曲。R本人自食惡果不足惜饮戳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望洞拨。 院中可真熱鬧扯罐,春花似錦、人聲如沸烦衣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琉挖。三九已至启泣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間示辈,已是汗流浹背寥茫。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留矾麻,地道東北人纱耻。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像险耀,于是被迫代替她去往敵國(guó)和親弄喘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容