編舞模式將工作流程邏輯去中心化,並將責任分配給系統內的其他元件。 服務部門不再依賴中央協調者,而是決定何時以及如何處理業務運作。
內容和問題
通常你會將雲端應用程式拆分成幾個小型服務,這些服務協同運作以處理端到端的商業交易。 交易中的單一操作可能導致所有服務間多次點對點通話。 理想狀況下,這些服務是鬆散耦合的。 設計分散式、高效且可擴展的工作流程具有挑戰性,因為它涉及複雜的服務間溝通。
通訊的常見模式是使用集中式服務或 協調器。 傳入要求會流經協調器,因為它會將作業委派給個別的服務。 每個服務都完成了自己的責任,卻不了解整體工作流程。
你通常會以自訂軟體實作編排器模式,具備系統內服務職責的領域知識。 此方法的一個好處是,協調器能根據下游服務執行的個別操作結果整合交易狀態。
這種做法也帶來一些障礙。 新增或移除服務可能會中斷現有的邏輯,因為您需要重新連接通訊路徑的部分。 此相依性可讓協調器實作變得複雜且難以維護。 編排器可能會對工作負載的可靠性產生負面影響。 在負載情況下,它可能會導致效能瓶頸,並成為單一故障點(SPoF)。 它也可能會導致下游服務中的連鎖故障。
解決方案
將交易處理邏輯委派給各服務。 讓每個服務參與企業營運的通訊工作流程,並決定何時以及如何處理。
編舞模式減少了對集中化溝通流程的客製化軟體的依賴。 這些元件在彼此間編排工作流程時,實作共同邏輯,且彼此之間不直接溝通。
實作編舞的常見方式是使用訊息代理程式來緩衝要求,直到下游元件宣告並處理這些要求為止。 下圖顯示透過 發布者-訂閱者模式處理請求。
客戶端請求會在訊息代理中作為訊息佇列。
服務或用戶會輪詢經紀人,以判斷其是否能根據實作的業務邏輯處理該訊息。 經紀商也能向有興趣的訂閱者推送訊息。
每個訂閱服務依訊息指示執行其操作,並以操作成功或失敗訊息回應經紀人。
若操作成功,服務可將訊息推回同一佇列或不同佇列,讓其他服務在需要時繼續該工作流程。 如果作業失敗,訊息代理程式會與其他服務搭配運作,以補償該作業或整個交易。
問題和考慮
在決定如何實施此模式時,請考慮以下幾點:
處理故障可能很有挑戰性。 應用程式中的元件可能負責管理原子任務,並依賴系統的其他部分。 一個元件的故障可能影響其他元件,導致整體請求完成延遲。
要優雅地處理失敗,你必須實作故障處理邏輯,這會增加複雜度。 失敗處理邏輯,例如 補償交易,也容易發生故障。
此模式適合平行處理獨立業務運作的工作流程。 當編舞需要以序列進行時,工作流程可能會變得複雜。 例如,D服務只有在B和C服務成功完成作業後才可開始運作。
若服務數量快速增加,這種模式會帶來挑戰。 許多獨立的環節使服務間的工作流程變得複雜。 你必須持續使用 分散式追蹤 與相關識別碼來維持可觀察性。
在編排器主導的設計中,核心元件可以將韌性責任(例如暫態、非暫態及超時失敗的重試處理)委派給專門的韌性處理者。
當你在以編舞為基礎的設計中移除編排器時,後續元件就不再承擔韌性責任。 它們仍集中在韌性處理程序中。 但下游元件必須直接與該處理器通訊,這會增加點對點的通訊。
事件結構演進可能隨時間導致消費者出現重大變化。 在此模式中,多個獨立服務會處理相同的事件。 如果生產者更改事件的資料結構,可能會破壞依賴舊結構的下游消費者。 使用架構註冊表來管理事件契約,並在服務獨立演進過程中,使用向後相容的演進。
在重試或橫向擴展時,事件排序無法保證。應考慮冪等性設計,並依序重新發送訊息,以處理重複或錯序事件。
去中心化事件拓撲能在大規模上產生湧現行為。 當多個服務對彼此事件做出反應時,系統可能無意中產生反饋迴路或事件風暴。 一個小事件可能會引發一連串的下游反應。 為防止循環事件鏈,應使用事件過濾、消費者並發限制、限速及明確規則等保護措施。
使用此模式的時機
當下列情況時,請使用此模式:
下游元件會獨立地處理原子操作。 可以把這種模式想像成一種 「開火後忘記 」機制,元件執行不需要主動管理的任務。 當任務完成後,元件會向其他元件發送通知。
你預期會經常更新和更換這些元件。 這種模式讓你能以較少的努力修改應用程式,且對現有服務的干擾最小。
你使用無伺服器架構來進行簡單的工作流程。 元件可以是短暫的和事件驅動的。 當事件發生時,服務會建立執行任務的元件,完成該任務後則移除元件。
有界上下文之間的通訊需要跨領域邊界的鬆散耦合。 若在單一有界上下文內進行通訊,則應應用編排器模式。
中央調度器引入了性能瓶頸。
在下列情況下,此模式可能不適用:
應用程式很複雜,需要中央元件來處理共享邏輯,讓下游元件保持輕量。
元件間的點對點通訊是不可避免的。
你需要使用商業邏輯來整合所有下游元件處理的操作。
工作負載設計
評估如何在工作負載設計中使用編舞模式,以達成 Azure Well-Architected Framework 支柱所涵蓋的目標與原則。 下表提供此模式如何支援每個要素目標的指引。
| 支柱 | 此模式如何支援支柱目標 |
|---|---|
| 卓越營運有助於透過標準化流程和團隊凝聚力來提供工作負載品質。 | 這種模式中的分散式元件是自主的,設計上可替換,因此你可以調整工作負載,且對系統整體的改變較少。 - OE:04 工具和程式 |
| 效能效率 可透過調整、數據和程式碼的優化, 有效率地協助您的工作負載符合需求 。 | 當集中式協調流程拓撲發生效能瓶頸時,此模式提供替代方式。 - PE:02 容量規劃 - PE:05 縮放和分區 |
如果此模式在一個支柱內部引入取捨,請將它們與其他支柱的目標進行考量。
範例
此範例展示了編舞模式,透過建立事件驅動的雲端原生工作負載,將函式與微服務並行運行。 當客戶端要求寄送包裹時,工作負載會指派一台無人機。 包裹準備好由預定的無人機取走後,投遞程序便開始。 包裹在運送途中,工作負載會負責處理運送過程,直到收到已出貨的狀態。
資料擷取服務會接收客戶端請求並將其轉換為包含傳遞細節的訊息。 商務交易會在服務接收這些新訊息後開始。
單一用戶端商務交易需要三個不同的商務作業:
建立或更新套件。
指派無人機送包裹。
負責配送,包括檢查並在包裹出貨時發送通知。
包裹、無人機排程器及配送微服務負責業務處理。 這些服務之間使用訊息傳遞,而非中央協調器來彼此溝通。 每個服務都必須事先實作一套協議,以去中心化的方式協調業務工作流程。
Design
服務會依序透過多跳處理業務交易。 每個跳點在所有商業服務間共用一條訊息匯流排。
當用戶端透過 HTTP 端點發送傳送請求時,擷取服務會接收該請求,將其轉換成訊息,然後將訊息發佈到共享訊息匯流排。 訂閱的商業服務會讀取新增到匯流排上的訊息。 當商業服務收到訊息時,這表示操作已成功完成、請求失敗或逾時。若請求成功,服務會以狀態碼Ok 回應訊息匯流排,產生新的操作訊息並將其送到訊息匯流排。 若請求失敗或逾時,服務會將原因碼傳送至訊息匯流排,然後將訊息加入死信佇列(DLQ)以回報失敗。 服務也會將無法在特定時間內接收或處理的訊息移至 DLQ。
此設計利用多重訊息匯流排來處理整個商業交易。 Azure Service Bus 與 Azure Event Grid 提供此設計的訊息服務平台。 該工作負載運行於 Azure Container Apps,該容器會承載 Azure Functions 供擷取。 容器應用程式處理執行業務邏輯的 事件驅動處理 。
這種設計也確保編舞是有順序的。 單一服務匯流排命名空間包含一個主題,該主題包含兩個訂閱及一個會話感知佇列。 資料引入服務會向主題名稱發布訊息。 套件服務與無人機排程服務會訂閱主題並發布訊息,通知隊列成功請求。 加入一個共用的會話識別碼,將 GUID 與傳遞識別碼關聯起來,讓服務能依序處理無界的相關訊息序列。 配送服務會等待每筆交易的兩則相關訊息。 第一個訊息表示包裹已準備好出貨,第二個則表示無人機已排程。
在此設計中,服務匯流排處理高價值訊息,且在整個傳遞過程中不得遺失或重複。 當套件出貨時,狀態變更會發布到 Event Grid。 事件發送者對狀態變更的處理方式沒有預期。 此設計未包含的下游組織服務可監聽此事件類型並執行特定商業邏輯,例如向使用者發送訂單狀態電子郵件。
如果你將此模式部署於其他運算服務,例如 AKS,你可以在同一個 pod 中實作 Publisher-Subscriber 模式應用樣板,並包含 兩個容器。 其中一個容器執行Ambassador,與您選擇的訊息匯流排互動,另一個容器則執行業務邏輯。 此方法提升效能與可擴展性。 大使與商業服務共用同一網路,降低延遲並提升吞吐量。
為避免連鎖重試操作導致多次嘗試,企業服務應立即標記不可接受訊息。 透過使用通用原因碼或定義的應用程式碼來豐富這些訊息,使服務能將它們移至 DLQ。 考慮實作 Saga 模式來管理下游服務的一致性問題。 例如,另一個服務僅透過執行補償、重試或樞紐交易來處理死信訊息以進行修復。
商業服務是冪等的,以確保重試操作不會產生重複資源。 例如,套件服務會使用新增或更新操作將資料加入資料存儲。
下一步
透過 Azure Event Hubs 的架構登錄 來集中管理事件結構,以維持服務演進的相容性。
檢視 Azure 中的非同步訊息選項 ,了解用於實施去中心化工作流程的不同基礎設施選擇。
評估不同平台的技術能力,選擇最適合你特定編舞需求的 Azure 訊息服務 。
相關資源
在你的編舞設計中考慮以下這些模式:
利用 大使模式模組化商業服務。
實作基於佇列的載荷調平模式來處理工作負載的尖峰。
透過 Publisher-Subscriber 模式使用非同步分散式訊息。
如果一個或多個相關操作失敗,則使用 補償交易 來撤銷一系列成功的操作。