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