事件驅動架構由產生事件串流的 事件生產者 、接聽這些事件的 事件取用者 ,以及將事件從生產者傳輸至取用者的 事件通道 (通常實作為事件代理程式或擷取服務) 所組成。
Architecture
事件以近乎即時的方式傳遞,因此消費者可以在事件發生時立即做出回應。 生產者與消費者脫鉤,這意味著生產者不知道哪些消費者在聽。 消費者也彼此脫鉤,每個消費者都會看到所有事件。
此程式與 競爭消費者模式不同。 在競爭取用者模式中,取用者會從佇列中提取訊息。 假設沒有錯誤,每則訊息只會處理一次。 在某些系統中,例如 Azure IoT,必須大量擷取事件。
事件驅動架構可以使用 發佈-訂閱模型 或事件串流模型。
發佈-訂閱: 發佈-訂閱傳訊基礎架構會追蹤訂閱。 發佈事件時,它會將事件傳送給每個訂閱者。 活動傳遞後,就無法重播,新訂閱者也看不到活動。 建議您將 Azure 事件方格 用於發佈-訂閱案例。
事件串流: 事件會寫入記錄檔。 事件在分割區內嚴格排序,且具有持久性。 用戶端不會訂閱串流。 相反地,用戶端可以從資料流程的任何部分讀取。 用戶端負責提升其在串流中的位置,這表示用戶端可以隨時加入,並可以重播事件。 Azure 事件中樞 是專為高輸送量事件串流所設計。
在消費者方面,有一些常見的變化:
簡單的事件處理: 事件會立即觸發取用者中的動作。 例如,您可以搭配事件方格觸發程式或 Azure 服務匯流排觸發程式搭配使用 Azure Functions,以便在發佈訊息時執行程式碼。
基本事件關聯性: 取用者會處理一些離散的商業事件,透過識別碼將它們相互關聯,並保存來自早期事件的資訊,以便在處理後續事件時使用。 NServiceBus 和 MassTransit 等程式庫支援此模式。
複雜事件處理: 取用者會使用 Azure 串流分析 之類的技術來分析一系列事件,並識別事件資料中的模式。 例如,您可以彙總內嵌裝置在一段時間內的讀數,並在移動平均值超過特定閾值時產生通知。
事件串流處理: 使用資料串流平台,例如 Azure IoT 中樞、 事件中樞或 適用於 Apache Kafka 的事件中樞,作為管線來擷取事件,並將其饋送至串流處理器。 串流處理器會處理或轉換串流。 應用程式的不同子系統可能有多個串流處理器。 此方法非常適合 IoT 工作負載。
事件的來源可能位於系統外部,例如 IoT 解決方案中的實體裝置。 在此情況下,系統必須能夠以資料來源所需的數量和輸送量擷取資料。
有兩種主要方法可以建構事件承載。 當您控制事件取用者時,您可以決定每個取用者的承載結構。 此策略可讓您在單一工作負載中視需要混合方法。
在承載中包含所有必要的屬性:當您希望取用者擁有所有可用資訊而不需要查詢外部資料來源時,請使用此方法。 不過,由於多個 記錄系統,它可能會導致資料一致性問題,尤其是在更新之後。 合約管理和版本控制也可能變得複雜。
在承載中僅包含金鑰: 在這種方法中,取用者檢索必要的屬性,例如主鍵,以獨立地從資料來源獲取剩餘資料。 此方法提供更好的資料一致性,因為它具有單一記錄系統。 不過,它的效能可能比第一種方法差,因為取用者必須經常查詢資料來源。 您對耦合、頻寬、合約管理或版本控制的擔憂較少,因為較小的事件和較簡單的合約可降低複雜性。
在上圖中,每種類型的取用者都會顯示為單一方塊。 為了避免取用者成為系統中的單一失敗點,通常會有取用者的多個實例。 可能還需要多個實例來處理事件的數量和頻率。 單一取用者可以處理多個執行緒上的事件。 如果必須依序處理事件或需要精確一次的語意,此設定可能會造成挑戰。 如需詳細資訊,請參閱 最小化協調。
許多事件驅動架構中有兩個主要拓撲:
代理拓撲: 元件會將事件廣播至整個系統。 其他元件要麼對事件採取行動,要麼忽略事件。 當事件處理流程相對簡單時,此拓蹼很有用。 沒有中央協調或協調流程,因此此拓撲可以是動態的。
此拓撲高度解耦,有助於提供可擴展性、響應能力和元件容錯能力。 沒有任何元件擁有或知道任何多步驟商業交易的狀態,而且會以非同步方式採取動作。 因此,分散式交易存在風險,因為沒有內建機制來重新啟動或重播它們。 您需要仔細考慮錯誤處理和手動干預策略,因為此拓蹼可能是資料不一致的來源。
調解器拓撲: 此拓蹼解決了代理拓蹼的一些缺點。 有一個事件調解器來管理和控制事件的流程。 事件調解器會維護狀態,並管理錯誤處理及重新啟動功能。 與分配管理系統拓蹼相反,調解器拓蹼中的元件會以指令的形式廣播出現項目,且僅播送至指定的通道。 這些通道通常是訊息佇列。 取用者應該處理這些命令。
此拓蹼提供更多控制、更好的分散式錯誤處理,以及可能更好的資料一致性。 然而,這種拓撲引入了組件之間的耦合增加,並且事件中介器可能成為瓶頸或可靠性問題。
使用此架構的時機
當符合下列條件時,您應該使用此架構:
多個子系統必須處理相同的事件。
需要具有最小時間延遲的即時處理。
需要複雜的事件處理,例如型樣比對或時間範圍的彙總。
需要大量和高速的數據,例如物聯網。
您需要將生產者和消費者分離,以實現獨立的可擴展性和可靠性目標。
優點
此架構提供下列優點:
- 生產者和消費者是脫鉤的。
- 沒有點對點整合。 將新消費者新增至系統很容易。
- 消費者可以在事件發生時立即做出回應。
- 它具有高度可擴展性、彈性和分散式。
- 子系統具有事件串流的獨立視圖。
挑戰
保證交付
在某些系統中,尤其是在 IoT 案例中,保證傳遞事件至關重要。
依序處理事件或僅處理一次
為了復原和延展性,每個取用者類型通常會在多個執行個體中執行。 如果必須在取用者類型內依序處理事件,或未實作 冪等訊息處理 邏輯,此程式可能會建立挑戰。
跨服務的訊息協調
商務程序通常有多個服務來發佈和訂閱訊息,以在整個工作負載中達成一致的結果。 您可以使用編排和 Saga 協調流程等工作流程模式來可靠地管理各種服務的訊息流程。
錯誤處理
事件驅動架構主要依賴非同步通訊。 非同步通訊帶來的常見挑戰是錯誤處理。 解決此問題的一種方法是使用專用的錯誤處理程式處理器。
當事件取用者遇到錯誤時,它會立即以非同步方式將有問題的事件傳送至錯誤處理程式處理器,並繼續處理其他事件。 錯誤處理程式處理器會嘗試解決問題。 如果成功,錯誤處理常式處理器會將事件重新提交至原始擷取通道。 如果失敗,處理器可以將事件轉發給管理員以進行進一步檢查。 當您使用錯誤處理程式處理器時,會不按順序處理重新提交的事件。
資料遺失
非同步通訊帶來的另一個挑戰是資料遺失。 如果任何元件在成功處理事件並將其移交給下一個元件之前崩潰,則該事件將被刪除,並且永遠不會到達最終目的地。 若要將資料遺失的機會降到最低,請保留傳輸中的事件,並只有在下一個元件確認收到事件時才移除或取消事件佇列。 這些功能稱為 用戶端確認模式 和 最後一個參與者支援。
傳統請求-回應模式的實作
有時,事件生產者需要事件消費者立即回應,例如在繼續訂單之前取得客戶資格。 在事件驅動架構中,可以使用 請求-回應訊息傳遞來實現同步通訊。
此模式是透過要求佇列和回應佇列來實作。 事件生產者會將非同步要求傳送至要求佇列,暫停該作業上的其他作業,並等待回覆佇列中的回應。 這種方法有效地將這種模式轉變為同步過程。 然後,事件取用者會處理要求,並透過回應佇列將回覆傳回。 這種方法通常使用會話ID進行跟踪,因此事件生產者知道回應佇列中的哪一則訊息與特定請求相關。 原始要求也可以在 回覆標頭或其他共同商定的自訂屬性中指定回應佇列的名稱,可能是暫時的。
維護適當數量的事件
產生過多的精細事件可能會使系統飽和並不堪重負。 事件量過大,就很難有效分析事件的整體流程。 當需要復原變更時,此問題會加劇。 相反地,過度合併事件也可能產生問題,進而導致事件取用者不必要的處理和回應。
為了達到適當的平衡,請考慮事件的後果,以及取用者是否需要檢查事件承載以確定其回應。 例如,如果您有合規性檢查元件,則可能只發佈兩種類型的事件就足夠了: 合規 和 不合規。 這種方法有助於確保只有相關的消費者處理每個事件,從而防止不必要的處理。
其他考慮
事件中要包含的資料量可能是影響效能和成本的重要考量。 您可以將處理所需的所有相關資訊直接放在事件中,以簡化處理程式碼並消除額外的查詢。 當您只向事件新增最少數量的資訊 (例如少數識別碼) 時,您可以減少傳輸時間和成本。 不過,此方法需要處理程式碼來擷取所需的任何額外資訊。 如需詳細資訊,請參閱 節食您的活動。
請求僅對請求處理元件可見。 但是,工作負載中的多個元件通常可以看到事件,即使這些元件不會取用它們或不打算取用它們。 要以「假設違規」的心態運作,請注意您在事件中包含哪些資訊,以防止意外的資訊暴露。
許多應用程式使用事件驅動架構作為其主要架構。 您可以將此方法與其他架構樣式結合,以建立混合式架構。 典型的組合包括 微服務 以及 管道和過濾器。 整合事件驅動架構,透過消除瓶頸並在高請求量期間提供 背壓 來增強系統效能。
特定網域 通常跨越多個事件生產者、取用者或事件通道。 對特定網域的變更可能會影響許多元件。