循序車隊模式

透過類別鍵將相關訊息分組,並依序處理每個群組,一次只一則訊息,同時平行處理不同群組。

這種模式解決了在每個邏輯群組內維持先入先出(FIFO)正確性與跨群組擴展並行處理之間的張力。 設計確保排序限制不會成為系統性的瓶頸。

內容和問題

應用程式通常需要依照訊息抵達的順序處理相關訊息,同時仍需擴充以應付增加的負載。 在分散式架構中,這項需求較難達成,因為工作者會獨立從共享佇列拉取訊息。 當多個工作者爭搶訊息時,如同競爭消費者模式中那樣,順序就會被打亂。

考慮一個訂單追蹤系統,接收一連串操作,例如建立訂單、新增交易、修改過去交易及刪除訂單。 每個訂單的操作必須依 FIFO 順序處理,因為錯誤順序會破壞訂單狀態。 然而,傳入佇列中的各項操作會穿插在多個訂單之間。 單一消費者強制全域訂單會成為瓶頸,多個消費者可能會錯序處理同一訂單的操作。

針對這個問題的直接方法都會以不同的方式失效:

  • 單一消費者。 單一消費者能維持訊息順序,因為它一次只處理一則訊息,但無法擴充以因應更高的吞吐量。

  • 多個競爭的消費者。 多個消費者透過平行拉取訊息來提高吞吐量,但會失去各群組內的訊息順序保證。 兩個工作者可能會擷取同一筆訂單的連續訊息,並以並行或錯誤順序處理它們,進而損毀訂單狀態。

Solution

序列護送模式將相關訊息劃分為類別,並依序處理每個類別,一次處理一則訊息,同時類別會平行處理。

此模式的運作方式是為每個訊息分配一個類別鍵,以識別其所屬群組。 訊息代理利用此金鑰將訊息分割成邏輯群組。 在每個群組內,經紀人會強制執行 FIFO 排序,使鎖定群組的消費者能嚴格依照其排隊順序接收訊息。 不同群組可同時由不同消費者處理,因此系統能橫向擴展,且不犧牲單一群組內的排序。

在 Azure 上,Azure 服務匯流排 訊息會話提供了此模式的內建實作。

下圖顯示了一般的順序車隊模式。

循序護航模式示意圖。圖中顯示一位生產者、一個中央佇列,以及三個消費者。

在佇列中,不同類別的訊息可能會交錯排列,如下圖所示。

圖示顯示單一佇列中四類交錯訊息。每個類別佔據自己的水平車道。

此模式帶來幾項主要優點:

  • 依群組排序處理。 每個類別內的訊息會嚴格依序處理,避免競賽條件、順序異常狀態突變及需要重新排序的變通方法。

  • 跨群體的水平尺度。 每個類別都是獨立的並行單元。 新增消費者可與活躍類別數量成比例增加吞吐量,且不違反訂購保證。

  • 生產者與消費者的脫鉤。 生產者在不知道哪個消費者會處理這些訊息,或會在何時處理的情況下,將訊息放入佇列。 消費者是獨立可擴展且可被替換的。

問題和考慮

當您決定如何實作此模式時,請考慮下列幾點:

  • 類別與規模單位。 判斷您可根據傳入訊息的哪項屬性進行橫向擴充。 類別鍵定義了平行的單位:每個不同的鍵值都成為一個獨立可處理的群組。 在訂單追蹤情境中,這個屬性就是訂單 ID。 選擇過粗的按鍵(例如所有訂單共用單一客戶 ID)會限制平行性;而選擇過細的按鍵則無法帶來有意義的排序效益。

  • 吞吐量限制。 評估你的目標訊息吞吐量。 由於此模式強制每個類別內的連續處理,每個類別的吞吐量受限於處理單一訊息的時間。 例如,透過非同步輸入輸出或批次下游寫入來優化每則訊息處理時間,因為這段時間直接決定每個類別的最大吞吐量。 如果你的整體吞吐量需求非常高,請重新評估在整個訊息生命週期內是否確有必要嚴格遵循先入先出(FIFO)的排序。 替代方案包括強制執行開始訊息與結束訊息以括號化序列,或在批次視窗內依時間戳排序訊息,然後將批次送入平行處理。

  • 服務功能。 確認您選擇的訊息中介是否支援在佇列或佇列類別內逐一處理訊息。 並非所有訊息服務都提供分割區內的會話層級鎖定或 FIFO 保證。 若經紀人不原生支援此功能,消費者必須自行實作協調邏輯,這增加了複雜度,並可能出現重複處理、訊息遺漏或順序錯序。 會話支援也可能限制訊息層級或 SKU 的選擇,這會影響成本。

  • 可進化性。 規劃如何新增訊息類別到系統中。 該模式必須容納類別基數的成長,且不需對消費者進行結構性改變。 例如,假設前述的分類帳系統是針對某位客戶的。 如果你需要新客戶的導入,應該可以新增一組帳本處理器,依客戶 ID 分配工作,而不必重新設計佇列拓撲。

  • 訊息傳遞順序錯亂。 由於生產者與代理程式之間的網路延遲會波動,在代理程式的工作階段排序機制生效之前,訊息可能會不按順序到達。 可以考慮使用序號來驗證每個類別的排序。 你也可以在交易的最後一則訊息中加入序列結束標誌,讓消費者能偵測序列是否完成。

  • 毒訊息處理。 在會話中反覆處理失敗的訊息會阻擋該會話中所有後續訊息,因為該模式強制執行嚴格的順序順序。 設計策略以偵測毒訊息,例如追蹤傳遞嘗試次數,並在設定重試閾值後將其移至 死符佇列 ,讓會話中剩餘訊息能繼續處理。

  • 經紀人可用性。 訊息代理是所有類別的共享依賴。 其可用性與耐用性直接影響紙樣的可靠性保證。 根據工作負載的可用性需求與預算,評估經紀人層級的韌性特性,如可用性區域與地理災難復原,因為較高耐久性的配置通常會增加成本。

  • 生產者金鑰正確性。 此模式假設製作者在每則訊息中正確設定類別金鑰(會話 ID)。 如果生產者設定錯誤的金鑰,無論是意外還是錯誤,訊息就會導向錯誤的會話,導致該群組的狀態被破壞。 驗證生產者是否一致地指派類別金鑰,若訊息誤導導致嚴重後果,考慮在消費者端加入金鑰驗證邏輯。

  • 操作複雜度。 監控基於會話的處理會增加比標準佇列消耗更多的營運負擔。 營運商需要查看會話積壓(活躍會話數量及每個會話待報訊息深度)以識別落後的類別。 死字母會話需要獨立的監控與修復工作流程,以調查失敗訊息、解決根本原因,並將修正後的訊息重播回會話中。

  • 會話鎖定爭用和延遲。 會話鎖定會帶來額外的延遲負擔,因為每個消費者都必須先取得會話的獨佔鎖定,才能處理訊息。 當消費者持有會話鎖定時,即使使用者速度緩慢或暫時停頓,其他消費者無法處理該會話的訊息。 若鎖定持續時間過短,鎖過期可能導致訊息重新處理。 如果鎖定時間過長,停擺的取用者會延遲復原。 根據預期的訊息處理時間調整工作階段鎖定持續時間,並為執行時間較長的作業實作鎖定續訂。

  • 消費端的擴展性與成本。 跨會話的平行性可轉化為同時的消費者實例。 在像 Azure Functions 這樣的無伺服器模型中,每個活躍會話對應到一個並行執行,而在專用模型中,則會對應到實例或執行緒。 因此,活動會話數量直接影響計算成本。 規劃消費者擴展限制與並發控制,以平衡吞吐量與成本。

使用此模式的時機

當下列情況時,請使用此模式:

  • 訊息依序抵達,且必須依照相同順序處理。
  • 訊息可以被分類,使每個類別成為系統獨立的規模單位。

在下列情況下,此模式可能不適用:

  • 你會預期極高的吞吐量(每分鐘數百萬則訊息),因為 FIFO 要求限制了系統可達成的擴展性。

  • 訊息排序並非必要。 當訊息可以任意順序獨立處理時, 競爭消費者模式 提供了更簡單的水平擴展,且不會因會話鎖定而產生協調負擔。

工作負載設計

評估如何在工作負載設計中運用 Sequential Convoy,以達成 Azure Well-Architected Framework 支柱所涵蓋的目標與原則。 下表提供此模式如何支援每個要素目標的指引。

支柱 此模式如何支援支柱目標
可靠性 設計決策有助於使工作負載具有韌性,並確保在故障發生後能復原到正常運作的狀態。 此模式使用以工作階段為基礎的先入先出(FIFO)排序,藉此消除競爭條件、容易發生爭用的訊息處理邏輯,以及其他為了因應訊息順序錯誤而採取、但可能導致故障的權宜作法。

- RE:02 關鍵流程
- RE:07 背景作業

如果此模式在一個支柱內部引入取捨,請將它們與其他支柱的目標進行考量。

範例

在 Azure 上,你可以透過使用 服務匯流排 訊息會話來實作這個模式。 對於消費者,你可以使用 Azure Logic Apps 搭配 服務匯流排 窺鎖連接器,或使用 Azure Functions 搭配 服務匯流排 觸發器

當產生者在訊息上設定該SessionId屬性時,服務匯流排 會將所有共享相同 session ID 的訊息群組成單一邏輯會話。 消費者接受會話並獲得專屬鎖定。 此鎖定保證同一時間只有一位消費者處理該會話的訊息,且訊息會依 FIFO 順序抵達。 其他使用者可以同時接受並處理不同的會話,提供跨群組的平行吞吐量。

以訂單追蹤為例,系統會依接收順序處理每則帳本訊息,並將每筆交易傳送至另一個佇列,且其類別設為訂單 ID。 在這種情況下,交易不會橫跨多筆訂單,因此消費者會並行處理各類別,但在類別內仍會依先入先出(FIFO)的順序處理。

帳本處理器會透過對第一個佇列中每則訊息的內容進行拆批,將訊息展開分送:

序列車隊範例架構示意圖。它顯示一個生產者、一個帳本佇列、一個帳本處理器、一個交易佇列,以及三個訂單處理器。

帳本處理器執行三個步驟:

  1. 逐筆遍歷分類帳中的交易。
  2. 將訊息的會話 ID 設定為與訂單 ID 相符。
  3. 將每個帳本交易傳送到次要佇列,會話 ID 設為訂單 ID。

消費者會監聽次級隊列,並以FIFO順序處理所有訂單ID相符的訊息。 取用者使用 預覽鎖定 模式。

帳本佇列是序列到平行的過渡點:所有交易依序通過該點,然後才擴散至基於會話的平行處理。 此序列化階段是主要的可擴展瓶頸,因為它限制了整個下游管線的吞吐量。 然而,在帳本處理器將訊息分送到次級佇列後,消費者便可在各個工作階段之間獨立擴展,每個訂單 ID 對應一個工作階段。

支援技術

貢獻者們

本文由 Microsoft 維護。 以下貢獻者撰寫了這篇文章。

主要作者:

若要查看非公開的 LinkedIn 個人檔案,請登入 LinkedIn。

  • 競爭的消費者模式:多個消費者並行從共享佇列拉取訊息,這提高了吞吐量,但取消了每則訊息排序的保證。 順序車隊模式解決了競爭消費者帶來的訂單落差。 它透過將訊息分割成類別鍵控會話,並依序處理每個會話來彌補此缺口。

  • 以佇列為基礎的負載調節模式:佇列會在生產者與消費者之間緩衝工作,以吸收突發性負載,並使不均勻的負載趨於平順。 Sequential Convoy 模式在此緩衝機制的基礎上加入工作階段型分區,使佇列既能平衡各類別之間的負載,又能保留各類別內的先進先出順序。

  • 優先佇列模式:訊息會被路由到不同的佇列,或在佇列內賦予優先權,使得優先權較高的工作先被處理,優先處理較低優先權的工作。 當必須保留優先級內的排序時,順序車隊模式可與優先排隊結合,在每個優先鍵控會話中強制執行先入先出(FIFO)處理。

  • Peek-Lock 訊息(非破壞性讀取):此操作從佇列或訂閱中原子式擷取並鎖定訊息以進行處理。

  • 在 Logic Apps 中使用 服務匯流排 工作階段依序傳遞相關訊息:這篇部落格文章說明 Logic Apps 對 Sequential Convoy 模式的支援。