前面我們介紹了發(fā)布訂閱的架構(gòu)設(shè)計(jì)方式,本文在此基礎(chǔ)上,繼續(xù)討論關(guān)于事件驅(qū)動系統(tǒng)的相關(guān)內(nèi)容。
概念介紹
事件驅(qū)動系統(tǒng),本質(zhì)上就是在微服務(wù)架構(gòu)的基礎(chǔ)上,依托于消息隊(duì)列,通過發(fā)布和訂閱的方式組織的系統(tǒng)。消息在這里稱為事件(event),發(fā)布者稱為生產(chǎn)者(publisher),訂閱者稱為消費(fèi)者(consumer)。最常見的事件驅(qū)動系統(tǒng)是git,通過例如commit的事件驅(qū)動所有的服務(wù)。下圖是一個事件驅(qū)動系統(tǒng)的架構(gòu)圖。

應(yīng)用場景
為什么要設(shè)計(jì)事件驅(qū)動系統(tǒng)呢?我們舉一個例子,假如有一款射擊游戲,有兩個玩家,在某一時刻,玩家A向玩家B所在的位置射擊,比如位置9,此時玩家B正好在位置9,應(yīng)該是可以擊中的。如果系統(tǒng)是采用請求響應(yīng)的方式設(shè)計(jì)的,玩家A向服務(wù)器發(fā)送一個請求,服務(wù)器在接收到這個請求時,玩家B已經(jīng)移動到了位置10,因此服務(wù)器判斷玩家A未擊中玩家B,結(jié)果是錯誤的。

如果用事件驅(qū)動的方式設(shè)計(jì),將所有的設(shè)計(jì)和移動都作為事件發(fā)送給服務(wù)器,并記錄事件發(fā)生的時間點(diǎn)。那在玩家A向服務(wù)器發(fā)送射擊事件時,服務(wù)器會回溯到玩家A發(fā)送請求的時間點(diǎn),獲取當(dāng)時玩家B的位置,并判斷射擊是否命中,這樣結(jié)果就是準(zhǔn)確的了。
事件保存
在事件驅(qū)動系統(tǒng)中,最好在每個服務(wù)自身的數(shù)據(jù)庫中記錄所有的事件。這并不是一個必須的做法,但如果這樣做的話,會使得每個服務(wù)對于事件的持久化要求,可以不依賴Event Bus。每個服務(wù)記錄的事件,可以結(jié)合服務(wù)的實(shí)際情況添加或者減少一些字段。我們介紹一下這種實(shí)現(xiàn)的優(yōu)缺點(diǎn)。
-
優(yōu)點(diǎn):
- 提高可用性:服務(wù)本身記錄了事件,可以載不依賴其他服務(wù)的情況下處理這些事件。
- 易回滾:當(dāng)服務(wù)出錯修改后,可以從服務(wù)記錄的事件中的任何一個重新處理這些事件。
- 方便服務(wù)替換:如果用另一個服務(wù)替換已有的服務(wù),可以在新服務(wù)上重新處理本地保存的所有事件,然后再從Event Bus接收新的事件,就完成了新服務(wù)的上線。
- 事務(wù)性:發(fā)送到Event Bus的消息可以提供兩種語義保證,At-least-once和At-most-once。
- 記錄意圖:可以記錄每個事件在服務(wù)中的變化,在開發(fā)新服務(wù)時,可以據(jù)此實(shí)現(xiàn)與之前不同的功能。
-
缺點(diǎn):
- 數(shù)據(jù)一致性:將事件記錄在本地的問題就是數(shù)據(jù)可能出現(xiàn)不一致的狀態(tài)。
- 網(wǎng)關(guān)服務(wù)不能替換:網(wǎng)關(guān)上的服務(wù)會處理和外界的請求和響應(yīng),這可能是和當(dāng)時請求的時間點(diǎn)有關(guān)的,因此不能像之前介紹的替換服務(wù)那樣,替換網(wǎng)關(guān)的服務(wù)。
- 可控性差:由于每個服務(wù)處理自己的事件,難以跟蹤一次請求的全部執(zhí)行過程。
- 處理過程不透明:每個服務(wù)之間沒有直接的聯(lián)系,將不容易理解一個請求的整體處理過程。
- 權(quán)限控制:使用Event Bus的話,無法控制有哪些服務(wù)會使用這些事件;或者如果服務(wù)只允許獲取部分事件,也是需要進(jìn)行額外控制的。
- 不好移植:如果想把事件驅(qū)動系統(tǒng),移植到請求響應(yīng)的系統(tǒng)中是很復(fù)雜的。
再提一下服務(wù)如何回到過去某個事件時的狀態(tài),可以采用的方法有:
- 回到開始事件并依此處理直到該事件。如果事件的數(shù)量特別多,這個方法是不太現(xiàn)實(shí)的。
- 記錄開始事件,以及后續(xù)事件的變化??梢詼p少事件的存儲量。執(zhí)行方式和上面的方法相同。
- 記錄每個事件撤回到前一個時間的撤銷操作,對于一些計(jì)算事件是可行的,但比如發(fā)郵件這種事件是無法撤回的。
一種解決方案是周期性的將事件進(jìn)行壓縮,比如每天或者每兩天,然后記錄當(dāng)時處理完成的狀態(tài)。這樣在想回到某個事件的狀態(tài)時,就可以查找最近的壓縮過的狀態(tài),然后再使用前兩種方法。
小結(jié)
事件驅(qū)動系統(tǒng)可以理解為是發(fā)布訂閱模式的應(yīng)用,通過在每個服務(wù)內(nèi)部記錄事件日志的方式,解決了服務(wù)難以回溯歷史事件的問題。事件驅(qū)動系統(tǒng)的優(yōu)點(diǎn)和缺點(diǎn)都比較突出,可以按實(shí)際需求選擇是否應(yīng)用。
歡迎大家訂閱專題,其中包含了系統(tǒng)設(shè)計(jì)基礎(chǔ)系列的全部文章:System Design