如果您是一名企業(yè)架構(gòu)師,您可能聽說(shuō)過(guò)微服務(wù)架構(gòu),并使用過(guò)它。雖然您過(guò)去可能使用REST作為服務(wù)通信層,但是越來(lái)越多的項(xiàng)目正在轉(zhuǎn)向事件驅(qū)動(dòng)的體系結(jié)構(gòu)。讓我們深入了解這種流行架構(gòu)的優(yōu)缺點(diǎn)、它所包含的一些關(guān)鍵設(shè)計(jì)選擇以及常見的反模式。
什么是事件驅(qū)動(dòng)的微服務(wù)體系結(jié)構(gòu)?
在事件驅(qū)動(dòng)的體系結(jié)構(gòu)中,當(dāng)服務(wù)執(zhí)行其他服務(wù)可能感興趣的某些工作時(shí),該服務(wù)將生成一個(gè)事件—執(zhí)行操作的記錄。其他服務(wù)使用這些事件,以便它們能夠執(zhí)行由于該事件而需要的任何自己的任務(wù)。與REST不同,創(chuàng)建請(qǐng)求的服務(wù)不需要知道使用請(qǐng)求的服務(wù)的詳細(xì)信息。
這里有一個(gè)簡(jiǎn)單的例子:當(dāng)一個(gè)訂單被放置在一個(gè)電子商務(wù)網(wǎng)站,一個(gè)單一的“訂單放置”事件產(chǎn)生,然后被幾個(gè)微服務(wù)消費(fèi):
1.order服務(wù),它可以向數(shù)據(jù)庫(kù)寫入一個(gè)order記錄。
2.客戶服務(wù),它可以創(chuàng)建客戶記錄。
3.支付服務(wù),它可以處理支付。
事件可以以多種方式發(fā)布。例如,可以將它們發(fā)布到保證將事件交付給適當(dāng)使用者的隊(duì)列中,也可以將它們發(fā)布到發(fā)布事件并允許訪問(wèn)所有相關(guān)方的“發(fā)布/訂閱”模型流中。在這兩種情況下,生產(chǎn)者發(fā)布事件,消費(fèi)者接收該事件,并做出相應(yīng)的反應(yīng)。注意,在某些情況下,這兩個(gè)角色還可以稱為發(fā)布者(生產(chǎn)者)和訂閱者(消費(fèi)者)。
為什么使用事件驅(qū)動(dòng)的體系結(jié)構(gòu)
與REST相比,事件驅(qū)動(dòng)架構(gòu)提供了以下幾個(gè)優(yōu)點(diǎn):
異步——基于事件的架構(gòu)是異步的,沒(méi)有阻塞。這使得資源可以在他們的工作單元完成后自由地轉(zhuǎn)移到下一個(gè)任務(wù),而不用擔(dān)心之前發(fā)生了什么或者接下來(lái)會(huì)發(fā)生什么。它們還允許對(duì)事件進(jìn)行排隊(duì)或緩沖,從而防止使用者向生產(chǎn)者施加壓力或阻塞它們。
•松耦合——服務(wù)不需要(也不應(yīng)該)知道或依賴于其他服務(wù)。在使用事件時(shí),服務(wù)獨(dú)立運(yùn)行,不了解其他服務(wù),包括其實(shí)現(xiàn)細(xì)節(jié)和傳輸協(xié)議。事件模型下的服務(wù)可以獨(dú)立地、更容易地更新、測(cè)試和部署。
•易于擴(kuò)展——由于服務(wù)在事件驅(qū)動(dòng)的體系結(jié)構(gòu)下解耦,而且服務(wù)通常只執(zhí)行一項(xiàng)任務(wù),因此跟蹤特定服務(wù)的瓶頸,并對(duì)該服務(wù)(且僅對(duì)該服務(wù))進(jìn)行擴(kuò)展變得很容易。
•恢復(fù)支持——帶有隊(duì)列的事件驅(qū)動(dòng)架構(gòu)可以通過(guò)“重播”過(guò)去的事件來(lái)恢復(fù)丟失的工作。當(dāng)用戶需要恢復(fù)時(shí),這對(duì)于防止數(shù)據(jù)丟失非常有用。
當(dāng)然,事件驅(qū)動(dòng)的架構(gòu)也有缺點(diǎn)。通過(guò)分離緊密耦合時(shí)可能更簡(jiǎn)單的關(guān)注點(diǎn),它們很容易過(guò)度設(shè)計(jì);它們可能需要大量的前期投資;而且常常導(dǎo)致基礎(chǔ)設(shè)施、服務(wù)契約或模式、多語(yǔ)言構(gòu)建系統(tǒng)和依賴關(guān)系圖的額外復(fù)雜性。
也許最大的缺點(diǎn)和挑戰(zhàn)是數(shù)據(jù)和事務(wù)管理。由于事件驅(qū)動(dòng)模型的異步性,它們必須小心處理服務(wù)之間不一致的數(shù)據(jù)、不兼容的版本、監(jiān)視重復(fù)的事件,并且通常不支持ACID事務(wù),而不支持最終的一致性,因?yàn)楹笳吒y以跟蹤或調(diào)試。
即使有這些缺點(diǎn),事件驅(qū)動(dòng)的體系結(jié)構(gòu)通常也是企業(yè)級(jí)微服務(wù)系統(tǒng)的更好選擇。主要的優(yōu)點(diǎn)是可伸縮的、松散耦合的、開發(fā)人員操作友好的。
何時(shí)使用REST
然而,有時(shí)REST/web接口可能仍然更可取:
•您需要一個(gè)異步請(qǐng)求/應(yīng)答接口。
•您需要對(duì)強(qiáng)事務(wù)的支持。
•您的API對(duì)公眾可用。
•您的項(xiàng)目很小(REST的設(shè)置和部署要簡(jiǎn)單得多)。
您最重要的設(shè)計(jì)選擇—消息傳遞框架
一旦決定了事件驅(qū)動(dòng)的體系結(jié)構(gòu),就該選擇事件框架了。事件生成和使用的方式是系統(tǒng)中的一個(gè)關(guān)鍵因素。目前已有數(shù)十種經(jīng)過(guò)驗(yàn)證的框架和選擇,選擇正確的框架需要時(shí)間和研究。
分倆個(gè)大類: 消息處理或流處理。
消息處理
在傳統(tǒng)的消息處理中,組件創(chuàng)建消息,然后將其發(fā)送到特定的(通常是單個(gè)的)目的地。一直處于空閑狀態(tài)并等待的接收組件接收消息并相應(yīng)地執(zhí)行操作。通常,當(dāng)消息到達(dá)時(shí),接收組件執(zhí)行單個(gè)流程。然后,刪除消息。
消息處理體系結(jié)構(gòu)的一個(gè)典型例子是消息隊(duì)列。盡管大多數(shù)較新的項(xiàng)目使用流處理(如下所述),但是使用消息(或事件)隊(duì)列的體系結(jié)構(gòu)仍然很流行。消息隊(duì)列通常使用代理的“存儲(chǔ)和轉(zhuǎn)發(fā)”系統(tǒng),事件在此系統(tǒng)中從一個(gè)代理傳遞到另一個(gè)代理,直到它們到達(dá)適當(dāng)?shù)氖褂谜摺ctiveMQ和RabbitMQ是消息隊(duì)列框架的兩個(gè)流行示例。這些項(xiàng)目都有多年的實(shí)踐經(jīng)驗(yàn)和成熟的技術(shù)社區(qū)。
流處理
另一方面,在流內(nèi)處理中,組件在達(dá)到某個(gè)狀態(tài)時(shí)發(fā)出事件。其他感興趣的組件在事件流中偵聽這些事件并作出相應(yīng)的反應(yīng)。事件不針對(duì)特定的收件人,而是對(duì)所有感興趣的組件可用。
在流內(nèi)處理中,組件可以同時(shí)對(duì)多個(gè)事件作出反應(yīng),并對(duì)多個(gè)流和事件應(yīng)用復(fù)雜的操作。有些流包括持久性,即事件在流上停留的時(shí)間可以根據(jù)需要延長(zhǎng)。
通過(guò)流處理,系統(tǒng)可以重現(xiàn)事件的歷史,在事件發(fā)生后聯(lián)機(jī)并仍然對(duì)其作出反應(yīng),甚至執(zhí)行滑動(dòng)窗口計(jì)算。例如,它可以從每秒的事件流計(jì)算每分鐘的平均CPU使用量。
最流行的流處理框架之一是Apache Kafka。Kafka是許多項(xiàng)目使用的成熟和穩(wěn)定的解決方案。它可以被認(rèn)為是一種工業(yè)強(qiáng)度的流處理解決方案。Kafka有一個(gè)龐大的用戶群、一個(gè)有用的社區(qū)和一個(gè)改進(jìn)的工具集。
其他的選擇
還有其他框架提供流和消息處理的組合,或者提供它們自己獨(dú)特的解決方案。例如,Apache的最新產(chǎn)品Pulsar是一個(gè)開源的發(fā)布/訂閱消息系統(tǒng),它支持流和事件隊(duì)列,所有這些都具有極高的性能。Pulsar的特點(diǎn)是豐富的-它提供多租戶和地理復(fù)制-因此復(fù)雜。據(jù)說(shuō)Kafka的目標(biāo)是高吞吐量,而脈沖星的目標(biāo)是低延遲。
NATS是另一種具有“合成”隊(duì)列的發(fā)布/訂閱消息系統(tǒng)。NATS是為發(fā)送小而頻繁的信息而設(shè)計(jì)的。它提供了高性能和低延遲;然而,NATS認(rèn)為某種程度的數(shù)據(jù)丟失是可以接受的,優(yōu)先考慮性能而不是交付保證。
其他的設(shè)計(jì)考慮
一旦你選擇了你的事件框架,這里有幾個(gè)其他的挑戰(zhàn)需要考慮:
•Event Sourcing
很難實(shí)現(xiàn)松耦合服務(wù)、不同的數(shù)據(jù)存儲(chǔ)和原子事務(wù)的組合。一個(gè)可能有所幫助的模式是事件源。在事件源中,從來(lái)不直接對(duì)數(shù)據(jù)執(zhí)行更新和刪除;相反,實(shí)體的狀態(tài)更改被保存為一系列事件。
•CQRS
上面的事件來(lái)源引入了另一個(gè)問(wèn)題:由于需要從一系列事件構(gòu)建狀態(tài),查詢可能會(huì)很慢,而且很復(fù)雜。命令查詢責(zé)任隔離(CQRS)是一種設(shè)計(jì)解決方案,它為插入操作和讀取操作調(diào)用單獨(dú)的模型。
•事件發(fā)現(xiàn)
事件驅(qū)動(dòng)體系結(jié)構(gòu)中最大的挑戰(zhàn)之一是對(duì)服務(wù)和事件進(jìn)行編目。在哪里可以找到事件描述和詳細(xì)信息?事件發(fā)生的原因是什么?是哪個(gè)團(tuán)隊(duì)創(chuàng)造了這個(gè)活動(dòng)?他們?cè)诜e極地工作嗎?
•應(yīng)對(duì)變化
事件模式會(huì)改變嗎?如何在不破壞其他服務(wù)的情況下更改事件模式?隨著服務(wù)和事件數(shù)量的增長(zhǎng),如何回答這些問(wèn)題變得至關(guān)重要。
成為一個(gè)好的事件消費(fèi)者意味著要為變化的模式編碼。成為一個(gè)好的事件生產(chǎn)者意味著要認(rèn)識(shí)到模式更改如何影響其他服務(wù),并創(chuàng)建經(jīng)過(guò)良好設(shè)計(jì)的事件,這些事件被清楚地記錄下來(lái)。
•內(nèi)部部署vs.托管部署
無(wú)論您的事件框架是什么,您還需要在自行部署框架(消息代理的操作并不簡(jiǎn)單,特別是在高可用性的情況下),還是使用托管服務(wù)(如Heroku上的Apache Kafka)之間做出選擇。
反模式
與大多數(shù)體系結(jié)構(gòu)一樣,事件驅(qū)動(dòng)的體系結(jié)構(gòu)具有自己的一組反模式。以下是一些需要注意的地方:
設(shè)計(jì)過(guò)多的事件
注意不要對(duì)創(chuàng)建事件過(guò)于興奮。創(chuàng)建太多的事件將在服務(wù)之間創(chuàng)建不必要的復(fù)雜性,增加開發(fā)人員的認(rèn)知負(fù)擔(dān),增加部署和測(cè)試的難度,并導(dǎo)致事件使用者的擁塞。不是每個(gè)方法都需要是一個(gè)事件。
通用的事件
不要使用通用事件,無(wú)論是在名稱中還是在目的上。您希望其他團(tuán)隊(duì)了解您的事件為何存在、應(yīng)該用于什么以及應(yīng)該在什么時(shí)候使用。事件應(yīng)該有特定的目的,并相應(yīng)地命名。事件與通用名稱或通用事件與混亂的旗幟,導(dǎo)致問(wèn)題。
復(fù)雜的依賴關(guān)系圖
注意那些相互依賴的服務(wù),并創(chuàng)建復(fù)雜的依賴關(guān)系圖或反饋循環(huán)。每個(gè)網(wǎng)絡(luò)跳都會(huì)給原始請(qǐng)求增加額外的延遲,特別是離開數(shù)據(jù)中心的南北網(wǎng)絡(luò)流量。
這取決于保證的訂單、交付或副作用
事件是異步的;因此,包含順序或重復(fù)的假設(shè)不僅會(huì)增加復(fù)雜性,而且會(huì)抵消基于事件的體系結(jié)構(gòu)的許多關(guān)鍵優(yōu)點(diǎn)。如果使用者有副作用,例如在數(shù)據(jù)庫(kù)中添加值,則可能無(wú)法通過(guò)重播事件進(jìn)行恢復(fù)。
過(guò)早優(yōu)化
大多數(shù)產(chǎn)品一開始很小,然后隨著時(shí)間的推移而增長(zhǎng)。雖然您可能夢(mèng)想將來(lái)需要擴(kuò)展到大型復(fù)雜組織,但是如果您的團(tuán)隊(duì)很小,那么事件驅(qū)動(dòng)架構(gòu)的額外復(fù)雜性實(shí)際上可能會(huì)降低您的速度。相反,考慮使用簡(jiǎn)單的體系結(jié)構(gòu)來(lái)設(shè)計(jì)系統(tǒng),但是要包含必要的關(guān)注點(diǎn)分離,以便您可以隨著需求的增長(zhǎng)將其替換掉。
期望事件驅(qū)動(dòng)來(lái)修復(fù)所有問(wèn)題
在較低的技術(shù)級(jí)別上,不要期望事件驅(qū)動(dòng)的體系結(jié)構(gòu)能夠修復(fù)所有的問(wèn)題。雖然這種體系結(jié)構(gòu)肯定可以改進(jìn)許多技術(shù)功能障礙的領(lǐng)域,但它不能解決核心問(wèn)題,比如缺乏自動(dòng)化測(cè)試、缺乏團(tuán)隊(duì)溝通或過(guò)時(shí)的開發(fā)-ops實(shí)踐。
理解事件驅(qū)動(dòng)架構(gòu)的優(yōu)缺點(diǎn),以及它們最常見的一些設(shè)計(jì)決策和挑戰(zhàn),是創(chuàng)建盡可能好的設(shè)計(jì)的重要部分。