如先前所述,當您使用 事件型通訊時, 微服務 會在發生值得注意的情況時發佈事件,例如當其更新商務實體時。 其他微服務會訂閱這些事件。 當微服務收到事件時,它可以更新自己的業務實體,這可能會導致更多事件被發佈。 這是最終一致性概念的本質。 此 發佈/訂閱 系統通常是使用事件總線的實作來執行。 事件總線可以設計為介面,其中包含訂閱和取消訂閱事件及發佈事件所需的 API。 它也可以根據任何進程間或傳訊通訊來擁有一或多個實作,例如支援異步通訊的傳訊佇列或服務總線,以及發行/訂閱模型。
您可以使用事件來實作跨越多個服務的商務交易,這可讓您在這些服務之間取得最終的一致性。 最終一致的交易是由一系列分散式動作所組成。 在每個動作中,微服務會更新商務實體,併發佈觸發下一個動作的事件。 請注意,事務不會延展至基礎持久性和事件總線,因此需要處理等冪性。 下圖 6-18 顯示透過事件總線發佈的 PriceUpdated 事件,因此價格更新會傳播至購物籃和其他微服務。
圖 6-18。 基於事件總線的事件驅動通訊
本節說明如何使用泛型事件總線介面來實作這種與 .NET 的通訊,如圖 6-18 所示。 有許多潛在的實作,每個實作都使用不同的技術或基礎結構,例如RabbitMQ、Azure服務總線或任何其他第三方開放原始碼或商業服務總線。
針對生產系統使用訊息代理程式和服務總線
如架構一節所述,您可以選擇多個傳訊技術來實作抽象事件總線。 但這些技術在不同層級。 例如,傳訊代理傳輸 RabbitMQ 低於 Azure 服務總線、NServiceBus、MassTransit 或 Brighter 等商業產品。 大部分的產品都可以在RabbitMQ或 Azure 服務總線上運作。 您選擇的產品取決於應用程式所需的功能數量及即用型擴展性。
僅為您的開發環境實作事件總線概念驗證,如同 eShopOnContainers 範例,在作為容器運行的 RabbitMQ 之上完成的簡單實作可能已足夠。 但對於需要高延展性的任務關鍵性和生產系統,您可能想要評估和使用 Azure 服務總線。
如果您需要高階抽象概念和像 Sagas 這樣的豐富功能,用於簡化長時間執行的分散式開發流程,那麼其它如 NServiceBus、MassTransit 和 Brighter 等商業和開放原始碼的服務總線也值得評估。 在此情況下,要使用的抽象和 API 通常直接是那些高階服務總線所提供的抽象概念,而不是您自己的抽象概念(例如 eShopOnContainers 所提供的簡單事件總線抽象概念)。 為此,您可以使用 NServiceBus 來研究分支 eShopOnContainers (特定軟體所實作的其他衍生範例)。
當然,您一律可以在RabbitMQ和 Docker 等較低層級的技術上建置自己的服務總線功能,但「重塑方向盤」所需的工作對於自定義企業應用程式來說可能太昂貴。
若要重申:eShopOnContainers 範例中所展示的範例事件總線抽象概念和實作僅供概念證明使用。 一旦您決定要進行異步和事件驅動通訊,如目前一節所述,您應該選擇最適合生產需求的服務總線產品。
整合事件
整合事件用於使多個微服務或外部系統的網域狀態保持同步。 這項功能是藉由在微服務外部發佈整合事件來完成。 當事件發佈至訂閱該整合事件的多個接收者微服務時,這些接收者微服務中各自的適當事件處理程式將會處理該事件。
整合事件基本上是數據存放類別,如下列範例所示:
public class ProductPriceChangedIntegrationEvent : IntegrationEvent
{
public int ProductId { get; private set; }
public decimal NewPrice { get; private set; }
public decimal OldPrice { get; private set; }
public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice,
decimal oldPrice)
{
ProductId = productId;
NewPrice = newPrice;
OldPrice = oldPrice;
}
}
整合事件可以在每個微服務的應用層級定義,因此它們與其他微服務分離,與 ViewModel 在伺服器和用戶端中定義的方式相當。 不建議在多個微服務之間共用一般整合事件連結庫;這麼做會將這些微服務與單一事件定義資料庫結合。 您不想要這樣做的原因與不想跨多個微服務共用通用領域模型的原因相同:微服務必須完全自主。 如需詳細資訊,請參閱此部落格文章 ,以瞭解要放入事件中的數據量。 請注意不要太過推進,因為另一篇部落格文章中描述了數據不足的問題訊息可能會產生的影響。 您的活動設計應該針對消費者的需求做到「恰到好處」。
只需在微服務之間共用幾種特定類型的共享庫。 其中之一是最終應用程式模組的函式庫,例如 eShopOnContainers 中的 事件總線用戶端 API。 另一個是程式庫,其中有些工具可以共用為 NuGet 元件,例如 JSON 序列化器。
事件總線
事件總線允許微服務之間的發佈/訂閱樣式通訊,而不需要元件明確感知彼此,如圖 6-19 所示。
圖 6-19。 使用事件總線發佈/訂閱基本概念
上圖顯示微服務 A 發佈至事件總線,其會散發至訂閱微服務 B 和 C,而發行者不需要知道訂閱者。 事件總線與 Observer 模式和發行-訂閱模式相關。
觀察者模式
在 觀察者模式中,您的主要物件(稱為 Observable)會通知其他感興趣的物件(稱為觀察者)相關信息(事件)。
發佈/訂閱 (Pub/Sub) 模式
發行/訂閱模式的目的與觀察者模式相同:您想要在特定事件發生時通知其他服務。 但是觀察者與 Pub/Sub 模式之間有一個重要的差異。 在觀察者模式中,廣播是直接從被觀察者(observable)傳送到觀察者(observers),因此它們彼此「知道」。 但是,使用 Pub/Sub 模式時,有第三個元件,稱為訊息代理程式或訊息代理程式或事件總線,發行者和訂閱者皆知。 因此,當使用 Pub/Sub 模式時,發行者和訂閱者會因為提及的事件總線或訊息代理程式而精確分離。
中介者或事件總線
如何實現發行者和訂閱者之間的匿名性? 一個簡單的方法是讓中間人照顧所有的溝通。 事件總線是一種中介。
事件總線通常由兩個部分組成:
抽象或接口。
一個或多個實作方案。
在圖 6-19 中,您可以看到從應用程式的觀點來看,事件匯流排只不過是 Pub/Sub 通道。 您實作這個異步通訊的方式可能會有所不同。 它可以有多個實作,以便您可以根據環境需求交換它們(例如生產環境與開發環境)。
在圖 6-20 中,您可以根據 RabbitMQ、Azure 服務總線或其他事件/訊息代理程式等基礎結構傳訊技術,查看具有多個實作的事件總線抽象概念。
圖 6- 20。 事件總線的多個實作
最好讓事件總線透過介面定義,以便透過數種技術來實作,例如RabbitMQ、Azure 服務總線或其他技術。 不過,如先前所述,只有在您需要抽象概念支援的基本事件總線功能時,使用您自己的抽象概念(事件總線介面)才不錯。 如果您需要更豐富的服務總線功能,您應該使用您慣用的商業服務總線所提供的 API 和抽象概念,而不是您自己的抽象概念。
定義事件總線介面
讓我們從事件總線介面的一些實作程式代碼開始,以及探索用途的可能實作。 介面應該是泛型且直接的,如下列介面所示。
public interface IEventBus
{
void Publish(IntegrationEvent @event);
void Subscribe<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>;
void SubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler;
void UnsubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler;
void Unsubscribe<T, TH>()
where TH : IIntegrationEventHandler<T>
where T : IntegrationEvent;
}
方法 Publish
很簡單。 事件總線會將接收到的整合事件廣播至任何已訂閱該事件的微服務,甚至是外部應用程式。 發佈事件的微服務會使用這個方法。
想要接收事件的微服務會使用多種方法(可以有多個實作,具體取決於所需的參數)。 這個方法有兩個參數。 第一個要訂閱的是整合事件(IntegrationEvent
)。 第二個自變數是名為 IIntegrationEventHandler<T>
的整合事件處理程式(或回呼方法),在接收者微服務取得該整合事件訊息時執行。
其他資源
某些生產就緒傳訊解決方案:
Azure 服務總線
https://learn.microsoft.com/azure/service-bus-messaging/NServiceBus
https://particular.net/nservicebusMassTransit
https://masstransit-project.com/