讓多個並行取用者處理相同傳訊通道上收到的訊息。 擁有多個同時使用者時,系統可同時處理多則訊息,以優化吞吐量、提升可擴展性與可用性,並平衡工作負載。
內容和問題
雲端應用程式通常會處理大量請求。 應用程式不必同步處理每個請求,而是可以透過訊息系統將請求傳遞給非同步處理的消費者服務。 此策略有助於防止請求處理阻塞應用程式的商業邏輯。
申請數量隨時間可能有顯著變化。 使用者活動突然增加或多個租戶的合併請求,都可能造成不可預測的工作負載。 在尖峰時段,系統可能需要每秒處理數百個請求。 有時,這個數字可能很小。 此外,處理這些請求所需的工作量可能差異很大。 如果你只使用單一的消費者服務實例,請求可能會導致該實例超負載。 或者大量應用程式訊息會讓訊息系統過載。
為了處理這種波動的工作負載,系統可以執行多個消費者服務實例。 然而,系統必須協調這些消費者,確保每則訊息只送達給一位消費者。 系統也必須平衡各消費者的工作負載,以防止單一實例成為瓶頸。
解決方案
使用訊息佇列實作應用程式與消費者服務實例之間的通訊通道。 應用程式將請求作為訊息發送到佇列,而消費者服務實例則接收並處理佇列的訊息。 此方法允許同一消費者服務實例池處理來自任一應用程式實例的訊息。 下圖展示了訊息佇列如何將工作分配給服務實例。
備註
多個消費者會收到這些訊息,但競爭消費者模式與 Publisher-Subscriber 模式不同。 在競爭消費者模式中,每則訊息由一位消費者接收進行處理。 在 Publisher-Subscriber 模式中, 所有 消費者都會收到 每一 則訊息。
此解決方案有下列優點:
它提供一個負載等級系統,能處理來自應用程式實例的請求量大幅變化。 佇列作為應用程式實例與消費者服務實例之間的緩衝區。 此緩衝區可將對應用與服務實例可用性與回應性的影響降至最低。 如需詳細資訊,請參閱 佇列型負載撫平模式。 需要長時間處理的訊息,並不會阻止其他消費者服務實例同時處理其他訊息。
其可改善可靠性。 如果生產者直接與消費者溝通,而非使用此模式且未監控消費者,當消費者失敗時,該產品將面臨訊息遺失或無法處理的機率。 在此模式中,系統不會將訊息傳送給特定的服務實例。 失敗的服務實例不會阻擋產生者,任何正常運作的服務實例都能處理訊息。
它不需要消費者之間或生產者與消費者實例間複雜的協調。 消息佇列可確保每個訊息至少傳遞一次。
可縮放規模。 當你套用 自動擴展時,系統可以隨著訊息量波動動態增加或減少消費者服務實例數量。
如果消息佇列提供交易式讀取作業,它可以改善復原能力。 如果消費者服務實例在交易操作中讀取並處理訊息但失敗,此模式可確保訊息會回傳至佇列,讓另一個消費者服務實例能處理。 為降低持續訊息失敗的風險,我們建議使用 死信佇列。
問題和考慮
在決定如何實施此模式時,請考慮以下幾點:
訊息排序: 消費者服務實例接收訊息的順序並不保證,也不一定顯示訊息產生的順序。 系統設計要能夠< c0>以冪等方式處理訊息
。 此設計有助於消除處理順序的依賴性。 Azure Service Bus 可透過 訊息會話實現訊息及其他模式的保證先入先出排序。
服務韌性要求: 如果系統偵測並重啟失敗的服務實例,可能需要將這些服務實例執行的操作視為冪等,以減少在多次檢索和處理同一訊息時的影響。
毒性訊息偵測:錯誤的訊息或需存取不可用資源的任務,都可能導致服務實例出現故障。 系統應防止這些訊息無限期返回佇列,並在必要時將其資料擷取並儲存於其他地方以便分析。 服務匯流排可在送達次數超過設定閾值後,自動將訊息送至
MaxDeliveryCount。結果處理: 處理訊息的服務實例與產生訊息的應用程式邏輯完全解耦,因此可能無法直接通訊。 如果服務實例產生的結果必須回溯到應用程式邏輯,請將這些資訊儲存在兩個元件都能存取的位置。 為防止應用程式邏輯取得不完整的資料,系統必須標示處理完成時間。 工作程序可透過專用的訊息回覆佇列將結果回傳給應用程式邏輯。 應用程式邏輯必須能夠將這些結果與原始訊息相互關聯。
訊息系統擴展: 在大規模解決方案中,大量訊息可能會壓垮單一訊息佇列,使其成為系統瓶頸。 在這種情況下,可以考慮將訊息系統分割,從特定產生者傳送訊息到特定佇列,或負載平衡將訊息分散到多個訊息佇列。
訊息系統的可靠性: 使用可靠的訊息系統,確保應用程式將訊息加入佇列後不會遺失。 此功能對於確保所有訊息至少送達一次至關重要。
使用此模式的時機
當下列情況時,請使用此模式:
應用程式工作負載被劃分為可非同步執行的任務。
工作是獨立的,而且可以平行執行。
工作量變化很大,需要可擴展的解決方案。
解決方案必須提供高可用性,並在任務處理失敗時保持韌性。
在下列情況下,此模式可能不適用:
你無法輕易將應用程式工作量拆分成獨立任務,或者任務間存在高度依賴性。
任務必須同步執行,且應用程式邏輯必須等待每個任務完成後才能繼續。
任務必須依特定順序執行。
備註
部分訊息系統支援會話,讓生產者將訊息分組,並確保同一消費者同時處理群組中所有訊息。 當支援時,你可以將此機制與優先訊息結合,強制訊息排序,並依序從生產者向單一消費者傳送訊息。
工作負載設計
評估如何在工作負載設計中使用競爭消費者模式,以達成 Azure Well-Architected Framework 支柱所涵蓋的目標與原則。 下表提供此模式如何支援每個要素目標的指引。
| 支柱 | 此模式如何支援支柱目標 |
|---|---|
| 可靠性 設計決策有助於使工作負載具有韌性,並確保在故障發生後能復原到正常運作的狀態。 | 此模式會將取用者視為複本來建置佇列處理中的備援,因此實例失敗不會防止其他取用者處理佇列訊息。 - RE:05 備援 - 背景工作 |
| 成本優化 專注於 維持並提升 您的工作負載的 投資報酬率。 | 此模式有助於優化成本,因為它會根據排隊深度調整,且在排隊空缺時可縮減至零。 它也能優化成本,因為你可以限制同時發生的消費者實例數量。 - CO:05 速率優化 - CO:07 元件成本 |
| 效能效率 可透過調整、數據和程式碼的優化, 有效率地協助您的工作負載符合需求 。 | 此模式將負載分配至消費節點以提升利用率,並基於佇列深度的動態擴展減少過度配置。 - PE:05 縮放和分區 - PE:07 程式代碼和基礎結構 |
如果此模式在一個支柱內部引入取捨,請將它們與其他支柱的目標進行考量。
範例
Azure 提供服務匯流排佇列與 Azure 函式佇列觸發器,兩者共同直接實作此雲端設計模式。 Functions 通過觸發器和綁定整合到服務匯流排。 這個整合讓您建立函式來處理來自發布端的佇列訊息。 發佈應用程式會將訊息貼入佇列,作為函式實作的使用者可以檢索並處理這些訊息。
為了增強韌性,服務總線佇列讓消費者在從佇列中擷取訊息時,使用 PeekLock 模式 。 此模式保留訊息,但對其他消費者隱藏。 "Functions 執行階段會以 PeekLock 模式接收訊息。" 如果函式成功完成,執行時會在該訊息上呼叫 Complete。 如果函式失敗,執行時可能會呼叫 Abandon 並再次顯示訊息,讓其他使用者能取得。 如果函式執行時間超過PeekLock逾時,執行時會自動更新鎖定,只要函式持續執行。
函式會根據 佇列深度與流量自動調整消費者實例數量。 這種擴展功能讓解決方案能夠在工作量激增時進行處理,同時在低使用量時期降低成本。 如果函式建立多個實例,它們會透過獨立拉取和處理訊息來競爭。 欲了解更多資訊,請參閱 服務匯流排佇列、主題與訂閱 ,以及 服務匯流排功能觸發器。
欲了解更多如何使用 .NET 的服務匯流排用戶端函式庫將訊息傳送至服務匯流排佇列的資訊,請參閱已發表的 範例。
下一步
選擇 Azure 中的訊息服務:了解不同的 Azure 訊息服務如 Service Bus、Azure Storage Queues、Azure Event Hubs 及 Azure Event Grid 如何支援非同步通訊模式,以及如何為你的情境選擇合適的服務與訊息模式。
自動擴展最佳實務:學習如何設計解決方案,根據工作負載(如佇列長度或訊息吞吐量)擴展消費者實例,讓您能在低活動期間處理高峰負載並控制成本。
相關資源
計算資源整合模式:你或許能將多個消費者服務實例整合為單一流程,以降低成本和管理負擔。 計算資源整合模式描述了此方法的優點與取捨。
基於佇列的負載平衡模式:訊息佇列能為系統增添韌性。 韌性讓服務實例能處理來自應用程式實例的各種請求量。 訊息佇列作為緩衝區,負責平衡負載。 佇列型負載撫平模式會更詳細地描述此案例。