背景許多人都聽過異步編程,同步編程,但是好像對響應(yīng)式編程這個(gè)新名稱感到很陌生,即使我們在實(shí)際項(xiàng)目中已經(jīng)在使用這種編程方式。 這篇文章就是來介紹響應(yīng)式編程(Reactive Progamming)到底是怎么回事。 如果我們在 Google 中輸入 Reactive Programming,試圖了解這到底是怎樣的一種編程模式,我們很快會發(fā)現(xiàn)大量的文章,但是看完我們就會迷糊,概念還是不是那么清晰。這可能是因?yàn)樵S多介紹文章對領(lǐng)域的強(qiáng)關(guān)聯(lián)導(dǎo)致的。比如最常見的一種介紹是用一種數(shù)據(jù)流(data stream)的方式來展現(xiàn),還有就是前端領(lǐng)域的控件事件流,雖然這些場景是響應(yīng)式編程適合的,但是對我來說,總覺得還是不夠直觀。因?yàn)槲颐鎸Φ氖欠植际较到y(tǒng)的編程場景。 其實(shí)響應(yīng)式編程沒必要跟具體的應(yīng)用領(lǐng)域關(guān)聯(lián),它是一個(gè)可以普遍適用的概念和編程模型。 同步與異步分布式網(wǎng)絡(luò)系統(tǒng)中,各個(gè)參與方節(jié)點(diǎn)的運(yùn)行是相互獨(dú)立的,沒有共享內(nèi)存,沒有全局時(shí)鐘。各節(jié)點(diǎn)通過消息來進(jìn)行溝通。在傳統(tǒng)的理念中,我們會把這樣的網(wǎng)絡(luò)根據(jù)他們通信方式描述成同步和異步的。簡單來說,同步網(wǎng)絡(luò)是對消息的到達(dá)時(shí)間有限定要求(time bounded),以便保證網(wǎng)絡(luò)活動的確定性。而異步的網(wǎng)絡(luò),則對消息的到達(dá)沒有任何限制。即使發(fā)出的消息丟失了,也不會損害網(wǎng)絡(luò)的活性。用一個(gè)具體的例子來理解就是,節(jié)點(diǎn) A 發(fā)送了一個(gè)消息給節(jié)點(diǎn) B,期待得到 B 的回復(fù),以便通過某個(gè)決議或者完成某件后續(xù)事情,這個(gè)依賴等待的要求,就是同步網(wǎng)絡(luò)。因?yàn)樗蕾囈粋€(gè)答復(fù)才能進(jìn)行后續(xù)動作。換句話說,如果一個(gè)節(jié)點(diǎn)發(fā)送消息之后,不依賴另一個(gè)節(jié)點(diǎn)的答復(fù)也能正常運(yùn)行,那么就是異步網(wǎng)絡(luò)。 在同步網(wǎng)絡(luò)中,如果 B 由于網(wǎng)絡(luò)原因(掉線,或者CPU繁忙,等等)沒有在限定時(shí)間內(nèi)回復(fù),A 既不能確定消息已經(jīng)發(fā)送給 B,也無法確定后續(xù)步驟什么時(shí)候才能開始,因此,網(wǎng)絡(luò)活動變得不可預(yù)測,無法結(jié)束,也就是沒有了確定性。業(yè)界已經(jīng)證明了,在實(shí)際的網(wǎng)絡(luò)中,如果不對網(wǎng)絡(luò)條件施加任何限制,那么網(wǎng)絡(luò)確定性是永遠(yuǎn)無法達(dá)到的。因此,為了保證網(wǎng)絡(luò)活動的確定性,我們通常會網(wǎng)絡(luò)條件施加一些限制,比如,最典型的就是消息到達(dá)的時(shí)間限制。 解釋了同步網(wǎng)絡(luò)和異步網(wǎng)絡(luò)的區(qū)別,我們再來看同步編程和異步編程,其實(shí)我們接觸這兩個(gè)編程模式很久了。同步編程簡單來說就是,發(fā)出一個(gè)任務(wù),然后等待執(zhí)行。而異步編程就是,發(fā)出一個(gè)任務(wù),不等待結(jié)果,就繼續(xù)發(fā)出下一個(gè)任務(wù)。至于上一個(gè)任務(wù)的執(zhí)行結(jié)果,我們可以通過兩種方式獲得,一個(gè)是主動的輪訓(xùn),另一個(gè)是被動的接收反饋。由于在異步編程中,我們從不等待執(zhí)行結(jié)果,就可以進(jìn)行其他任務(wù)(前提是這個(gè)任務(wù)本身不依賴上一個(gè)任務(wù)的結(jié)果)。如果要執(zhí)行的某個(gè)其他任務(wù)依賴于上一個(gè)任務(wù)的結(jié)果,那么我們可以每隔一段時(shí)間輪訓(xùn)一次,或者另外開一個(gè)線程去等待接收任務(wù)結(jié)果。無論哪種情況,我們的網(wǎng)絡(luò)都不會阻塞在某一個(gè)單獨(dú)的任務(wù)上。 響應(yīng)式編程現(xiàn)在,我們很自然的過渡到響應(yīng)式編程(Reactive Programming)這個(gè)概念上,它是一種基于事件模式的模型。在上面的異步編程模式中,我們描述了兩種獲得上一個(gè)任務(wù)執(zhí)行結(jié)果的方式,一個(gè)就是主動輪訓(xùn),我們把它稱為 Proactive 方式。另一個(gè)就是被動接收反饋,我們稱為 Reactive。 簡單來說,在 Reactive 方式中,上一個(gè)任務(wù)執(zhí)行結(jié)果的反饋就是一個(gè)事件,這個(gè)事件的到來將會觸發(fā)下一個(gè)任務(wù)的執(zhí)行。 這也就是 Reactive 的內(nèi)涵!我們把處理和發(fā)出事件的主體稱為 Reactor,它可以接受事件并處理,也可以在處理完事件后,發(fā)出下一個(gè)事件給其他 Reactor。兩個(gè) Reactors 之間沒有必然的強(qiáng)耦合,他們之間通過消息管道來傳遞消息。Reactor 可以定義一些事件處理函數(shù),根據(jù)接收到的事件不同類型來進(jìn)行不同的處理。如果我們的系統(tǒng)復(fù)雜,我們還可以專門定義不同功能類別的 Reactors,分別處理不同類型的事件。而在每個(gè) Reactor 中對事件又進(jìn)行細(xì)分處理。 需要強(qiáng)調(diào)的是,實(shí)現(xiàn) Reactive 模型最核心的是線程和消息管道。線程用于偵聽事件,消息管道用于 Reactor 之間通信不同的消息。與他們相關(guān)的是事件管理器用于注冊、注銷事件,而消息分配器則會根據(jù)消息類型分發(fā)。 下面是一個(gè) Reactive 模型的示意圖: ![]() 術(shù)語理解在了解了上述異步編程模型的本質(zhì)之后,我們再來看一些我們常見的術(shù)語,就會發(fā)現(xiàn)一切都變得清晰明了了。 比如,依賴鏈(Dependency chain),假定我們有一個(gè)事件依賴鏈?zhǔn)沁@樣:睡覺 -> 吃飯 -> 餓了,很直覺的是,在這個(gè)依賴鏈中,只有滿足了后面的條件,前面的才會執(zhí)行。 這種依賴鏈?zhǔn)沁@個(gè)世界普遍的一種場景,一種正向的處理模式是,每隔一段時(shí)間就輪訓(xùn)檢測是否滿足睡覺的條件,在檢查是否能睡覺的時(shí)候,會先檢查是否已經(jīng)吃飯,檢查是否已經(jīng)吃飯的時(shí)候,又會先檢查是否餓了。那么這就是 Proactive 模式!而 Reactive 模式則反過來,先有事件的觸發(fā),然后事件來到響應(yīng)方,響應(yīng)方進(jìn)行處理,這種方式也叫 Pub/Sub 模式。我們在 OOP 語言中,也會用到同樣的概念和邏輯,我們把之叫做 Observer 模式,而在 Funcaitonal Programming 中,也有同樣的概念,它可以用 monads 來實(shí)現(xiàn)。 比如,我們有一個(gè) Publisher ,會產(chǎn)生 “餓” 事件,同時(shí)還有一個(gè)或多個(gè) Subscriber,在收到 “餓” 事件的發(fā)生之后,進(jìn)行響應(yīng)(比如更新狀態(tài)或者作出其他預(yù)先注冊好的行為)。 總的來說,Reactive Programming 就是編寫關(guān)于怎么響應(yīng)事件的編程模式,這些事件包括:用戶輸入,數(shù)據(jù)流,系統(tǒng)狀態(tài)的變化等等。 總結(jié)一下,響應(yīng)式編程通常會用在一個(gè)事件流相關(guān)的場景中,在一個(gè)事件流中,一旦觸發(fā)第一個(gè)事件,后續(xù)的事件會被依次觸發(fā),就像一個(gè) Pipeline 系統(tǒng),不斷有輸入和輸出。 響應(yīng)式編程的設(shè)計(jì)與實(shí)現(xiàn)接下來,講一下實(shí)現(xiàn)上的架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)。 通常 Reactor 的數(shù)量可以是預(yù)先定義的,因?yàn)橐粋€(gè)系統(tǒng),我們通常可以約束它處理哪些預(yù)定義的事件,比如有處理網(wǎng)絡(luò)連接的 Reactor,處理日志收集的 Reactor,處理數(shù)據(jù)存儲的 Reactor 等等,各司其職。而錯(cuò)誤(未定義)事件則可以單獨(dú)放在一個(gè)專門處理 Error/Exception 的 Reactor 中。通過事件管理器,每個(gè) Reactor 可以根據(jù)要發(fā)出或者接收的消息,即時(shí)地創(chuàng)建一個(gè)線程/協(xié)程去執(zhí)行響應(yīng)的操作。發(fā)出和接收消息可以根據(jù)業(yè)務(wù)的復(fù)雜度,分開單獨(dú)線程,也可以放在一個(gè)線程。這樣的設(shè)計(jì)架構(gòu)簡單而清晰。 下面是一個(gè)簡單的示意圖: ![]() |
|
|