在本文中,我們將學(xué)習(xí)如何使用事件驅(qū)動(dòng)的架構(gòu)模式,并將其應(yīng)用于Axon框架。讓我們開(kāi)始吧。
給定問(wèn)題
一般而言,在談?wù)撈髽I(yè)解決方案時(shí),會(huì)選擇客戶端-服務(wù)器結(jié)構(gòu)而不是其他模型??蛻舳撕头?wù)器通常位于單獨(dú)的硬件上,因此,為了從服務(wù)器訪問(wèn)資源,將需要在某種網(wǎng)絡(luò)上建立通信通道。

因此,要在其他系統(tǒng)之間進(jìn)行通信,我們的情況將如下圖所示。

然后,我們有這種請(qǐng)求驅(qū)動(dòng)的體系結(jié)構(gòu)的缺點(diǎn)。
- 如果任何系統(tǒng)中存在某些錯(cuò)誤,則很難跟蹤日志。
- 當(dāng)我們有多個(gè)其他系統(tǒng)時(shí),將它們集成在一起是一個(gè)難題。
- 獨(dú)立擴(kuò)展系統(tǒng)的某些部分很困難。
連續(xù)地,單片應(yīng)用程序通常使用分層的方法來(lái)構(gòu)造我們的代碼,其中每一層都有特定的功能。兩層應(yīng)用程序的結(jié)構(gòu)將包括一個(gè)由用戶界面表示的表示層,該表示層將任務(wù)轉(zhuǎn)換為用戶可以理解的內(nèi)容。數(shù)據(jù)層是信息存儲(chǔ)和檢索的地方,通常是數(shù)據(jù)庫(kù)或其他某種存儲(chǔ)解決方案。還有所有業(yè)務(wù)邏輯所在的業(yè)務(wù)層。該層通過(guò)處理和評(píng)估用戶界面給出的命令來(lái)控制應(yīng)用程序的功能。

在這種情況下,業(yè)務(wù)層由多個(gè)模塊組成,例如訂單,促銷,用戶,付款和建議。當(dāng)功能數(shù)量增加時(shí),應(yīng)用程序的復(fù)雜性也會(huì)增加,維護(hù)和開(kāi)發(fā)它的成本可能會(huì)更高。
為了解決這個(gè)問(wèn)題,出現(xiàn)了一種新的模式,微服務(wù)。在微服務(wù)體系結(jié)構(gòu)中,應(yīng)用程序分解為一組松散耦合的服務(wù),其中每個(gè)服務(wù)或模型都解決了一個(gè)特定問(wèn)題,并通過(guò)簡(jiǎn)單,輕量級(jí)的協(xié)議與其他服務(wù)進(jìn)行通信。微服務(wù)架構(gòu)可以輕松支持多個(gè)客戶端,例如Web或移動(dòng)客戶端,而無(wú)需任何額外開(kāi)銷。

考慮到先前定義的模型,將它們轉(zhuǎn)換為微服務(wù)模式將產(chǎn)生獨(dú)立的服務(wù)。訂單將接受并處理訂單。建議,根據(jù)購(gòu)物車中的當(dāng)前物品,它可以建議其他物品。用戶,負(fù)責(zé)授權(quán)客戶。付款處理和管理付款。而促銷,根據(jù)一些特定的規(guī)則,客戶可以從折扣中受益。
為了確保松散耦合,每個(gè)服務(wù)都有一個(gè)數(shù)據(jù)庫(kù)是必不可少的。而且,每個(gè)服務(wù)都可以使用最合適的數(shù)據(jù)庫(kù)類型。服務(wù)通過(guò)某種輕量級(jí)的通信協(xié)議(通常是REST API或RPC)進(jìn)行互連。要為客戶端提供一個(gè)單一的入口點(diǎn),需要一個(gè)API網(wǎng)關(guān)。API網(wǎng)關(guān)會(huì)將來(lái)自客戶端的所有請(qǐng)求轉(zhuǎn)發(fā)到適當(dāng)?shù)姆?wù),但同時(shí)可以包括一些功能,例如身份驗(yàn)證或監(jiān)視。對(duì)于復(fù)雜的系統(tǒng),微服務(wù)是一個(gè)很好的解決方案。但是,正如我們?cè)趫D上看到的那樣,盡管系統(tǒng)變得越來(lái)越復(fù)雜,但是服務(wù)之間的依賴關(guān)系也導(dǎo)致了微服務(wù)地獄的出現(xiàn)。
所有這些都是從客戶端發(fā)出的單個(gè)請(qǐng)求開(kāi)始的,但是為了完成請(qǐng)求,整個(gè)系統(tǒng)都會(huì)觸發(fā)許多其他請(qǐng)求,這使得在整個(gè)系統(tǒng)中跟蹤和管理我們的數(shù)據(jù)變得非常困難。為了解決這個(gè)問(wèn)題,需要一種不同的思維方式。因此,事件驅(qū)動(dòng)的體系結(jié)構(gòu)應(yīng)運(yùn)而生。
事件驅(qū)動(dòng)架構(gòu)模式的解決方案
事件驅(qū)動(dòng)的體系結(jié)構(gòu)是一種軟件體系結(jié)構(gòu)模式,可促進(jìn)事件的產(chǎn)生,檢測(cè),使用和響應(yīng)。換句話說(shuō),在事件驅(qū)動(dòng)的體系結(jié)構(gòu)中,一切都圍繞事件而不是數(shù)據(jù)。在EDA中可以遇到多種編程模型。在它們當(dāng)中,最受歡迎的仍然是微服務(wù)模式,其中將每個(gè)微服務(wù)集中于處理某些類型的事件,并使用基于代理的技術(shù)將它們分離。

事件驅(qū)動(dòng)的微服務(wù)架構(gòu)可以很好地解決許多問(wèn)題。無(wú)服務(wù)器應(yīng)用程序和現(xiàn)在流行的云計(jì)算服務(wù)Faas-功能即服務(wù)可以解決需要小型且壽命短的應(yīng)用程序的問(wèn)題。由于公司需要更快地響應(yīng)客戶的需求,因此如今的流媒體模型非常流行。在流傳輸過(guò)程中,事件將在事件到達(dá)系統(tǒng)時(shí)進(jìn)行處理,因此我們可以將流視為永無(wú)止境的操作,這使公司幾乎可以實(shí)時(shí)做出反應(yīng)。事件源意味著在基于日志重新創(chuàng)建當(dāng)前狀態(tài)時(shí),將數(shù)據(jù)存儲(chǔ)為一系列事件。嘗試對(duì)日記進(jìn)行成像,在該日記中,我們記帳他們用現(xiàn)金進(jìn)行的所有交易,而不是每次進(jìn)行交易時(shí)都更改當(dāng)前余額。
CQRS代表命令查詢責(zé)任隔離,它通過(guò)分離用于讀寫的接口來(lái)工作。在傳統(tǒng)系統(tǒng)中,讀取數(shù)據(jù)和寫入數(shù)據(jù)都是通過(guò)同一組實(shí)體執(zhí)行的。使用CQRS,接口將被分離并通過(guò)不同的API公開(kāi)。
以下是EDA中通常使用的一些概念。
-
消息是通信的基本單位,它實(shí)際上可以是任何東西。它可以是字符串,數(shù)字或完整的對(duì)象。消息可以描述為沒(méi)有特殊意圖的通用接口。
-
事件是一條消息,它通知各種聽(tīng)眾有關(guān)已發(fā)生的事情。
事件的真實(shí)示例是在廣告板上發(fā)布廣告。發(fā)布事件的人將被稱為生產(chǎn)者,然后被稱為消費(fèi)者的感興趣方將訂閱該廣告,然后對(duì)發(fā)布的事件做出反應(yīng)。應(yīng)該始終以過(guò)去式來(lái)引用事件,因?yàn)樗x了已經(jīng)發(fā)生的事情或已經(jīng)觸發(fā)的事情。
域事件是系統(tǒng)中正在發(fā)生且與業(yè)務(wù)相關(guān)的事件。通常,一個(gè)域事件會(huì)引起反應(yīng),甚至可能引發(fā)另一個(gè)事件,而我們的系統(tǒng)需要對(duì)此做出反應(yīng)。
-
一個(gè)命令將通過(guò)在生產(chǎn)者和消費(fèi)者之間建立一對(duì)一的連接來(lái)提出有針對(duì)性的行動(dòng)。例如,訂購(gòu)炸玉米餅可以看作是一個(gè)命令,因?yàn)槲覀兺ㄟ^(guò)呼叫甚至使用我們最喜歡的家庭輔助設(shè)備來(lái)下訂單,因此系統(tǒng)之間存在相互作用,該相互作用知道如何到達(dá)另一個(gè)。呼叫一個(gè)隨機(jī)數(shù)來(lái)訂購(gòu)炸玉米餅是很奇怪的。
何時(shí)使用
- 當(dāng)我們的系統(tǒng)必須處理多個(gè)請(qǐng)求時(shí),這將花費(fèi)大量時(shí)間來(lái)檢查業(yè)務(wù)邏輯。
好處與缺點(diǎn)
-
好處
-
所有組件都彼此分離。
當(dāng)組件需要通信時(shí),假設(shè)服務(wù)A需要向服務(wù)B傳輸一些數(shù)據(jù),那么它將使用基于代理的技術(shù)。代理通過(guò)促進(jìn)兩個(gè)系統(tǒng)之間的數(shù)據(jù)傳輸而充當(dāng)中間件,因此,他們唯一需要了解的就是代理的位置。服務(wù)A和服務(wù)B之間的直接通信將被禁止,因?yàn)檫@將導(dǎo)致緊密耦合的情況。
-
封裝形式
事件可以在不同的功能范圍內(nèi)進(jìn)行分類,并且可以在相同的范圍內(nèi)進(jìn)行處理。例如,在典型的電子商務(wù)系統(tǒng)中,我們將遇到與分析,付款,促銷,建議甚至身份驗(yàn)證相關(guān)的事件。每種類型的事件之間都有明確的界限,而不必?fù)?dān)心混淆它們。
-
速度
事件驅(qū)動(dòng)的體系結(jié)構(gòu)旨在通過(guò)對(duì)傳入事件到達(dá)系統(tǒng)時(shí)的反應(yīng)做出幾乎實(shí)時(shí)的運(yùn)行。
讓我們比較一下具有事件驅(qū)動(dòng)功能的傳統(tǒng)系統(tǒng),以及它們?cè)诔R?jiàn)情況(例如處理通知)中的反應(yīng)方式。在一個(gè)典型的系統(tǒng)中,用戶會(huì)告訴應(yīng)用程序-“讓我知道您什么時(shí)候又有這件令人敬畏的T恤庫(kù)存嗎?”。當(dāng)庫(kù)存增加時(shí),用戶可能必須等待排定的工作來(lái)掃描所有想要在該物品重新入庫(kù)時(shí)通知的人員。該作業(yè)完成后,將通知用戶。
在事件驅(qū)動(dòng)的情況下,用戶將通過(guò)發(fā)送一個(gè)名為notification_enabled的事件來(lái)告訴系統(tǒng)他希望得到通知。庫(kù)存量增加,傳輸稱為stock_updated的事件,并通知用戶。
-
可擴(kuò)展性
在事件驅(qū)動(dòng)的體系結(jié)構(gòu)中,可伸縮性自然而然地使應(yīng)用程序可以容納越來(lái)越多的工作,并在不需要它們時(shí)減少資源。
例如,欺詐檢測(cè)服務(wù),其工作是檢測(cè)是否有任何交易被視為可疑并最終被停止。隨著傳入事件數(shù)量的增加,事情變得越來(lái)越慢,某些請(qǐng)求可能永遠(yuǎn)不會(huì)被處理,甚至更糟的是,整個(gè)系統(tǒng)可能會(huì)凍結(jié)。最簡(jiǎn)單的解決方案是能夠水平擴(kuò)展欺詐檢測(cè)以處理事件增長(zhǎng)的能力。
-
-
缺點(diǎn)
設(shè)計(jì)和開(kāi)發(fā)事件驅(qū)動(dòng)的應(yīng)用程序需要陡峭的學(xué)習(xí)曲線才能上手,因?yàn)樗岢隽艘恍﹤鹘y(tǒng)架構(gòu)本質(zhì)上已經(jīng)給出答案的問(wèn)題。其中一些問(wèn)題可能是,在事件驅(qū)動(dòng)的體系結(jié)構(gòu)中,什么可以被認(rèn)為是真理的源頭?另一個(gè)問(wèn)題可能是,如果在某個(gè)時(shí)間點(diǎn)出現(xiàn)問(wèn)題并且發(fā)生了重復(fù)事件該怎么辦?我們準(zhǔn)備好處理了嗎?
維護(hù)可能會(huì)變得非常復(fù)雜。簡(jiǎn)單的事件驅(qū)動(dòng)體系結(jié)構(gòu)是在多年前引入的,但是隨著時(shí)間的流逝,系統(tǒng)不斷發(fā)展,企業(yè)解決方案通常需要復(fù)雜的事件流,這可能會(huì)變得難以理解和維護(hù)。
-
失去交互能力
在傳統(tǒng)的解決方案中,進(jìn)入系統(tǒng)的請(qǐng)求可以解釋為事務(wù),并且如果在處理該請(qǐng)求期間出現(xiàn)任何問(wèn)題,則可以輕松還原事務(wù)。
在事件驅(qū)動(dòng)的系統(tǒng)中,一個(gè)請(qǐng)求將導(dǎo)致一個(gè)或多個(gè)事件分散在整個(gè)系統(tǒng)中,從而在發(fā)生故障時(shí)幾乎不可能還原由它們觸發(fā)的動(dòng)作。
在調(diào)試我們的系統(tǒng)時(shí),沿襲是一個(gè)重要的主題。事件可能會(huì)丟失或損壞,并且由于應(yīng)用程序松散耦合,因此幾乎不可能確定事件從哪個(gè)系統(tǒng)到達(dá)。一個(gè)簡(jiǎn)單的解決方案是將某種標(biāo)識(shí)符固定到要傳遞給的每個(gè)應(yīng)用程序中的事件。
代碼C ++ / Java / Javascript
為了了解事件驅(qū)動(dòng)架構(gòu)的實(shí)現(xiàn)方式,我們將使用Axon框架進(jìn)行處理。
我們可以在Github上參考事件驅(qū)動(dòng)架構(gòu)的源代碼。




