シーケンシャルなコンボイ パターン

関連するメッセージをカテゴリ キーでグループ化し、各グループを一度に 1 つずつ順番に処理しながら、異なるグループを並列で処理します。

このパターンは、各論理グループ内で先入れ先出し (FIFO) の正確性を維持することと、グループ間で同時処理をスケールアウトする間の緊張を解決します。 この設計により、順序の制約がシステム全体のボトルネックにならないようにします。

コンテキストと問題

アプリケーションでは、多くの場合、負荷の増加を処理するためにスケールアウトしながら、到着した順序で関連するメッセージを処理する必要があります。 分散アーキテクチャでは、ワーカーが独立して共有キューからメッセージをプルするため、この要件を実現するのは困難です。 競合コンシューマー パターンのように、複数のワーカーがメッセージを取り合う場合、順序性は失われます。

注文の作成、トランザクションの追加、過去のトランザクションの変更、注文の削除などの操作のストリームを受け取る注文追跡システムについて考えてみましょう。 各注文の操作は FIFO の順序で処理する必要があります。順序を外して適用すると、注文の状態が破損するためです。 ただし、受信キューは、多数のオーダーにまたがる操作をインターリーブします。 グローバル注文を適用する 1 つのコンシューマーがボトルネックになり、複数のコンシューマーが同じ注文の操作を順番外に処理する可能性があります。

この問題に対する簡単なアプローチは、それぞれ異なる方法で分割されます。

  • 1 つのコンシューマー。 1 つのコンシューマーは一度に 1 つのメッセージを処理するため、メッセージの順序を保持しますが、スループットの向上を処理するようにスケーリングすることはできません。

  • 複数の競合コンシューマー。 複数のコンシューマーは、メッセージを並列にプルすることでスループットをスケーリングしますが、グループごとの順序付けの保証は失われます。 2 人のワーカーは、同じ順序で連続するメッセージをプルし、それらを同時にまたは順番外に処理できるため、順序の状態が破損します。

ソリューション

シーケンシャル コンボイ パターンは、関連するメッセージをカテゴリに分割し、各カテゴリを一度に 1 つのメッセージを順番に処理しますが、カテゴリは並列で処理されます。

このパターンは、各メッセージに、それが属するグループを識別するカテゴリ キーを割り当てることで機能します。 メッセージ ブローカーは、このキーを使用して、メッセージを論理グループにパーティション分割します。 各グループ内では、ブローカーは FIFO 順序を保証し、グループをロックしたコンシューマーがメッセージをエンキューされた順序どおりに厳密に受信できるようにします。 異なるグループを異なるコンシューマーが同時に処理できるため、1 つのグループ内の順序を損なうことなく、システムはグループ間で水平方向にスケーリングされます。

Azureでは、メッセージ セッションAzure Service Busこのパターンの組み込み実装が提供されます。

次の図は、一般的なシーケンシャル コンボイ パターンを示しています。

シーケンシャル コンボイ パターンの図。プロデューサー、中央キュー、および 3 つのコンシューマーが表示されます。

キューでは、次の図に示すように、さまざまなカテゴリのメッセージがインターリーブされる可能性があります。

1 つのキュー内の 4 つのカテゴリのインターリーブ メッセージを示す図。各カテゴリは、独自の水平レーンを占有します。

このパターンには、いくつかの主な利点があります。

  • グループごとの順序付け処理。 各カテゴリ内のメッセージは厳密に順番どおりに処理されるため、レースコンディションや順不同の状態変更を防ぎ、並べ替えのための回避策も不要になります。

  • グループ間での水平方向のスケール。 各カテゴリは、コンカレンシーの独立した単位です。 コンシューマーを追加すると、順序の保証を損なうことなく、アクティブなカテゴリの数に比例してスループットが向上します。

  • プロデューサーとコンシューマーの切り離し。 プロデューサーは、どのコンシューマーがいつ処理するかを知らずにメッセージをエンキューします。 コンシューマーは、独立してスケーラブルで置き換え可能です。

問題と考慮事項

このパターンの実装方法を決めるときには、以下の点に注意してください。

  • カテゴリとスケール ユニット。 スケールアウトできる受信メッセージのプロパティを決定します。 カテゴリ キーは並列処理の単位を定義します。各個別のキー値は、個別に処理可能なグループになります。 注文追跡シナリオでは、このプロパティは注文 ID です。 粗すぎるキー (たとえば、すべての注文に対して 1 つの顧客 ID) を選択すると並列処理が制限されますが、細かいキーを選択しても意味のある順序付けの利点はありません。

  • スループットの制限。 ターゲット メッセージのスループットを評価します。 このパターンでは各カテゴリ内で順次処理が強制されるため、カテゴリごとのスループットは、1 つのメッセージを処理する時間によって制限されます。 たとえば、非同期 I/O を使用したり、ダウンストリームの書き込みをバッチ処理したりして、メッセージごとの処理時間を最適化します。これは、その時間によって各カテゴリの最大スループットが直接決定されるためです。 全体的なスループット要件が非常に高い場合は、メッセージ ライフサイクル全体に厳密な FIFO 順序が必要かどうかを再検討します。 別の方法としては、開始メッセージと終了メッセージを強制してシーケンスを角かっこで囲んだり、バッチ ウィンドウ内のタイムスタンプでメッセージを並べ替えたり、並列処理のためにバッチを送信したりできます。

  • サービス機能。 選択したメッセージ ブローカーが、キューまたはキューのカテゴリ内のメッセージの一度に 1 回限りの処理をサポートしているかどうかを確認します。 すべてのメッセージング サービスが、パーティション内でセッション レベルのロックまたは FIFO の保証を提供するわけではありません。 ブローカーがこの機能をネイティブにサポートしていない場合、コンシューマーは独自の調整ロジックを実装する必要があります。これにより、複雑さが増し、処理の重複、メッセージの見落とし、または順序が誤った実行のリスクが生じます。 セッションのサポートでは、メッセージング層または SKU の選択が制限される場合もあります。これはコストに影響します。

  • 進化可能性。 新しいカテゴリのメッセージをシステムに追加する方法を計画します。 パターンは、コンシューマーに構造的な変更を必要とせずに、カテゴリカーディナリティの増加に対応する必要があります。 たとえば、前に説明した台帳システムが 1 人の顧客に固有であるとします。 新しい顧客をオンボードする必要がある場合は、キュー トポロジを再設計することなく、顧客 ID ごとに作業を分散する一連の台帳プロセッサを追加できる必要があります。

  • 順不同のメッセージ配信。 ブローカーのセッション順序付けが有効になるまでに、プロデューサーとブローカーの間のネットワーク待ち時間が変動するため、メッセージが順不同で到着する可能性があります。 シーケンス番号を使用して、各カテゴリ内の順序を確認することを検討してください。 コンシューマーがシーケンスが完了したときに検出できるように、トランザクションの最後のメッセージにシーケンス終了フラグを含めることもできます。

  • 有害メッセージの処理。 セッション内で処理が繰り返し失敗するメッセージは、パターンによって厳密な順次順序が適用されるため、そのセッション内の後続のすべてのメッセージをブロックします。 配信試行回数の追跡など、有害メッセージを検出し、セッション内の残りのメッセージが処理を続行できるように、定義済みの再試行しきい値の後に 配信不能キュー に移動する戦略を設計します。

  • ブローカーの可用性。 メッセージ ブローカーは、すべてのカテゴリの共有依存関係です。 その可用性と耐久性は、パターンの信頼性の保証に直接影響します。 ワークロードの可用性要件と予算に基づいて、可用性ゾーンや geo ディザスター リカバリーなどのブローカー レベルの回復性機能を評価します。これは、通常、持続性の高い構成ではコストが増加するためです。

  • プロデューサーキーの正確性。 このパターンでは、プロデューサーがすべてのメッセージに対してカテゴリ キー (セッション ID) を正しく設定することを前提としています。 プロデューサーが誤って、またはバグのために間違ったキーを設定した場合、メッセージは間違ったセッションにルーティングされ、そのグループの状態が破損します。 プロデューサーが一貫してカテゴリ キーを割り当てることを検証し、誤ってルーティングされたメッセージの結果が重大な場合は、コンシューマーにキー検証ロジックを追加することを検討します。

  • 運用の複雑さ。 セッション ベースの処理を監視すると、標準のキュー消費よりも運用上のオーバーヘッドが増加します。 オペレーターは、遅れているカテゴリを識別するために、セッション バックログ (アクティブなセッションの数と各セッションで待機しているメッセージの深さ) を可視化する必要があります。 配信不能セッションでは、失敗したメッセージを調査し、根本原因を解決し、修正されたメッセージをセッションに再生するために、個別の監視と修復ワークフローが必要です。

  • セッションロックの競合とレイテンシ。 セッション ロックでは、各コンシューマーがメッセージを処理する前にセッションの排他ロックを取得する必要があるため、待機時間のオーバーヘッドが発生します。 コンシューマーがセッション ロックを保持している場合、コンシューマーが低速または一時的にストールしている場合でも、他のコンシューマーはそのセッションからのメッセージを処理できません。 ロック期間が短すぎると、ロックの有効期限が切れ、メッセージの再処理が発生する可能性があります。 ロック期間が長すぎると、停止したコンシューマーが復旧を遅らせます。 予想されるメッセージ処理時間に基づいてセッション ロック期間を調整し、実行時間の長い操作のためにロック更新を実装します。

  • コンシューマーのスケーリングとコスト。 セッション間の並列処理は、コンカレント コンシューマー インスタンスに変換されます。 Azure Functionsなどのサーバーレス モデルでは、アクティブな各セッションは同時実行にマップされ、専用モデルではインスタンスまたはスレッドにマップされます。 そのため、アクティブなセッションの数はコンピューティング コストに直接影響します。 スループットとコストのバランスを取るために、コンシューマースケーリングの制限とコンカレンシー制御を計画します。

このパターンを使用する場合

このパターンは次の状況で使用します。

  • メッセージは順番に到着し、同じ順序で処理する必要があります。
  • メッセージを分類して、各カテゴリがシステムの独立したスケール単位になるようにすることができます。

このパターンは、次の場合に適さない場合があります。

  • FIFO 要件によってシステムで実現できるスケーリングが制限されるため、スループットが非常に高いシナリオ (1 分あたり数百万メッセージ) が予想されます。

  • メッセージの順序は必要ありません。 メッセージを任意の順序で個別に処理できる場合、 競合コンシューマー パターン では、セッション ロックの調整オーバーヘッドなしで、より単純な水平スケーリングが提供されます。

ワークロード設計

ワークロードの設計でシーケンシャル コンボイを使用して、Azure Well-Architected Framework の柱で説明されている目標と原則に対処する方法を評価します。 次の表は、このパターンが各柱の目標をサポートする方法に関するガイダンスを示しています。

このパターンが柱の目標をサポートする方法
信頼性設計の決定は、故障に対するワークロードの回復性を高め、障害の発生後にワークロードを完全な機能状態に回復させるために役立ちます。 このパターンでは、セッション ベースの FIFO 順序を使用することで、競合状態、競合を起こしやすいメッセージ処理ロジック、ならびに誤動作につながる可能性のある誤った順序のメッセージに対処するためのその他の回避策を排除できます。

- RE:02 重要な流れ
- RE: 07バックグラウンドジョブ

このパターンによって柱内にトレードオフが生じる場合は、他の柱の目標に照らして検討してください。

Azureでは、Service Bus メッセージ セッションを使用してこのパターンを実装できます。 コンシューマーの場合は、Service Busピーク ロック コネクタでAzure Logic Appsを使用するか、Service Bus トリガーでAzure Functionsを使用できます。

プロデューサーがメッセージにSessionId プロパティを設定すると、Service Busは同じセッション ID を共有するすべてのメッセージを 1 つの論理セッションにグループ化します。 コンシューマーはセッションを受け入れ、そのセッションに対する排他的ロックを受け取ります。 このロックにより、そのセッションのメッセージをいつでも 1 つのコンシューマーのみが処理し、そのメッセージが FIFO 順に到着することを保証します。 他のコンシューマーは、異なるセッションを同時に受け入れて処理できるため、グループ間で並列スループットが提供されます。

注文追跡の例では、システムは受信した順序で各台帳メッセージを処理し、カテゴリが注文 ID に設定されている別のキューに各トランザクションを送信します。 このシナリオではトランザクションが複数の注文にまたがることはありません。そのため、コンシューマーは各カテゴリを並列で処理しますが、カテゴリ内の FIFO を処理します。

台帳プロセッサは、最初のキューの各メッセージの内容をバッチ解除することで、メッセージをファンアウトします。

シーケンシャル コンボイのサンプル アーキテクチャの図。プロデューサー、台帳キュー、台帳プロセッサ、トランザクション キュー、および 3 つの注文プロセッサが表示されます。

図は、5 つのステージにわたって左から右に流れます。 左端にはプロデューサーというラベルが付いたボックスがあります。 プロデューサーから「ledger queue」と書かれたボックスへ矢印が伸びている。 矢印は、台帳キューから、台帳プロセッサというラベルが付いたボックスを指しています。 矢印は、台帳プロセッサから、トランザクション キューというラベルが付いたボックスを指します。 トランザクション キューから、3 つの矢印が右側を指し示し、それぞれにセッション カテゴリのラベルが付けられます。 上矢印は注文 A トランザクションとラベル付けされ、注文プロセッサ A というラベルが付いたボックスを指します。中央の矢印は注文 B トランザクションにラベル付けされ、注文プロセッサ B というラベルが付いたボックスを指します。下矢印は注文 C トランザクションとラベル付けされ、注文プロセッサ C というラベルが付いたボックスを指します。この図は、シリアルから並列への遷移を示しています。プロデューサーは台帳キューと台帳プロセッサを介してすべてのメッセージを順番に送信し、台帳プロセッサは、トランザクション キューにエンキューする前に、各メッセージのセッション ID を対応する注文 ID に設定します。 トランザクション キューは、各注文のメッセージを対応する注文プロセッサのみにルーティングします。これにより、注文プロセッサ A、注文プロセッサ B、および注文プロセッサ C は、それぞれのセッションを並列かつ FIFO 順に使用できます。

台帳プロセッサは、次の 3 つの手順を実行します。

  1. 台帳を1トランザクションずつたどります。
  2. 注文 ID と一致するようにメッセージのセッション ID を設定します。
  3. セッション ID が注文 ID に設定されたセカンダリ キューに各台帳トランザクションを送信します。

コンシューマーはセカンダリ キューをリッスンし、一致する順序 ID を持つすべてのメッセージを FIFO の順序で処理します。 コンシューマーはピーク ロック モードを使用します。

台帳キューは、シリアルから並列への移行ポイントです。すべてのトランザクションは、セッション ベースの並列処理にファンアウトする前に順番にこれを通過します。 このシリアル化ステージは、ダウンストリーム パイプライン全体のスループットをゲートするため、主なスケーラビリティのボトルネックです。 ただし、台帳プロセッサがメッセージをセカンダリ キューにファンアウトした後、コンシューマーは、注文 ID ごとに 1 つずつ、セッション間で個別にスケーリングできます。

サポート テクノロジ

  • Service Bus メッセージ セッション: セッション ID でメッセージをグループ化し、各セッション内で FIFO 処理を適用します。 メッセージ セッションは、シーケンシャル コンボイ パターンを実装するための主要なAzure メカニズムです。

  • Azure Functions Service Bus トリガー: 関数インスタンスが一度に 1 つのセッションからのメッセージを処理できるようにするセッション ベースのトリガーをサポートします。

  • Logic Apps Service Bus コネクタ: ワークフロー ベースの処理でセッションが有効なキューを使用するためのピーク ロックサポートを備えたService Bus コネクタを提供します。

Contributors

Microsoft では、この記事を保持しています。 この記事を書いたのは、以下の寄稿者です。

主要著者:

公開されていない LinkedIn プロフィールを見るには、LinkedIn にサインインしてください。

  • 競合コンシューマー パターン: 複数のコンシューマーが共有キューから並列にメッセージをプルします。これにより、スループットは向上しますが、メッセージごとの順序付けの保証は削除されます。 シーケンシャル コンボイ パターンは、競合コンシューマーが導入する順序のギャップに対処します。 これは、メッセージをカテゴリ キー付きセッションにパーティション分割し、各セッションを順番に処理することで、このギャップに対処します。

  • キューベースの負荷平準化パターン: キューは、プロデューサーとコンシューマーの間で作業をバッファリングすることで、突発的な負荷を吸収し、不均一な負荷を平準化します。 シーケンシャル コンボイ パターンは、セッション ベースのパーティション分割を追加することで、このバッファリングに基づいて構築されます。これにより、キューの両方のレベルがカテゴリ間で読み込まれるため、各カテゴリ内の FIFO 順序が保持されます。

  • 優先順位キュー パターン: メッセージは、優先順位の高い作業が優先順位の低い作業の前に処理されるように、キュー内で個別のキューまたは優先順位にルーティングされます。 優先順位レベル内での順序付けも保持する必要がある場合は、順次コンボイ パターンを優先順位キューと組み合わせて、各優先順位キー付きセッション内で FIFO 処理を適用できます。

  • Peek-Lock メッセージ (非破壊的読み取り): この操作は、キューまたはサブスクリプションからメッセージをアトミックに取得してロックして処理します。

  • Service Bus セッションを使用した Logic Apps での相関メッセージの配信の順序: このブログ記事では、シーケンシャル コンボイ パターンに対する Logic Apps のサポートについて説明します。