各サービスが、中央オーケストレーターに依存するのではなく、業務処理をいつどのように実行するかをそれぞれ判断できるようにします。 このアプローチでは、ワークフロー ロジックが分散化され、システムのコンポーネント全体に責任が分散されます。
コンテキストと問題
通常は、クラウドベースのアプリケーションを、エンドツーエンドのビジネス トランザクションを処理するために連携するいくつかの小さなサービスに分割します。 トランザクション内で 1 つの操作を実行すると、すべてのサービス間で複数のポイントツーポイント呼び出しが発生する可能性があります。 理想的には、これらのサービスは疎結合されています。 複雑なサービス間通信を伴うため、分散型で効率的でスケーラブルなワークフローを設計するのは困難です。
通信の一般的なパターンは、集中化されたサービスまたはオーケストレーターとして使用するというものです。 受信要求は、それぞれのサービスに操作を委任するオーケストレーターを通過します。 各サービスは責任を果たしており、ワークフロー全体を認識していません。
通常、オーケストレーター パターンは、システム内のサービスの責任に関するドメイン知識を持つカスタム ソフトウェアとして実装します。 このアプローチの利点の 1 つは、オーケストレーターが、ダウンストリーム サービスが実行する個々の操作の結果に基づいてトランザクションの状態を統合できることです。
この方法にはいくつかの障害が生じることもあります。 サービスを追加または削除すると、通信パスの一部を再配線する必要があるため、既存のロジックが壊れる可能性があります。 この依存関係により、オーケストレーターの実装が複雑になり、メンテナンスが困難になります。 オーケストレーターは、ワークロードの信頼性に悪影響を及ぼす可能性があります。 負荷の下では、パフォーマンスのボトルネックが発生し、単一障害点 (SPoF) になる可能性があります。 オーケストレーターが失敗するか、オーバーロードされると、障害はすべての依存ダウンストリーム サービスに伝達されます。
ソリューション
サービス間でトランザクション処理ロジックを委任します。 各サービスが業務のコミュニケーション ワークフローに参加し、いつどのように処理するかを決定します。
コレオグラフィ パターンは、通信ワークフローを一元化するカスタム ソフトウェアへの依存関係を最小限に抑えます。 コンポーネントは、相互に直接通信することなく、ワークフローを相互に振り付ける一般的なロジックを実装します。
コレオグラフィを実装する一般的な方法は、ダウンストリーム コンポーネントが要求を取得して処理するまで要求をバッファリングするメッセージ ブローカーを使用することです。 次の図は、 パブリッシャー サブスクライバー モデルを介した要求処理を示しています。
クライアントは、メッセージ ブローカーのメッセージとしてキューを要求します。
サービスまたはサブスクライバーはブローカーをポーリングして、実装されたビジネス ロジックに基づいてメッセージを処理できるかどうかを判断します。 ブローカーは、そのメッセージに関心のあるサブスクライバーにメッセージをプッシュすることもできます。
サブスクライブされた各サービスは、メッセージが示し、操作の成功または失敗のメッセージでブローカーに応答するように、その操作を行います。
操作が成功した場合、サービスは同じキューまたは別のメッセージ キューにメッセージを発行して、必要に応じて別のサービスがワークフローを続行できるようにします。 操作が失敗した場合、サービスはエラー メッセージを発行します。 そのメッセージをサブスクライブするサービスは、失敗した操作またはトランザクション全体に対して定義済みの補正アクションを実行できます。
問題と考慮事項
このパターンを実装する方法を決定するときは、次の点を考慮してください。
複雑さの処理に失敗しました。 アプリケーション内のコンポーネントはアトミック タスクを管理し、システムの他の部分に依存する場合があります。 1 つのコンポーネントでエラーが発生すると、他のコンポーネントに影響が及び、要求全体の完了に遅延が発生する可能性があります。
エラーを適切に処理するには、エラー処理ロジックを実装します。これにより、複雑さが生まれます。 補正トランザクションなどのエラー処理ロジックでも、エラーが発生しやすくなります。
順次プロセス。 このパターンは、独立したビジネス操作を並行して処理するワークフローに適しています。 コレオグラフィを順番に実行する必要がある場合、ワークフローは複雑になる可能性があります。 たとえば、サービス D は、サービス B とサービス C が正常に操作を完了した後にのみ、その操作を開始できます。
大規模な可観測性。 このパターンは、サービスの数が急速に増加する場合に課題を提示します。 多くの独立した可動部分は、サービス間のワークフローを複雑にします。 完全なトランザクション状態を保持する中央オーケストレーターがない場合、1 つのコンポーネントには、処理中のビジネス操作の完全なビューはありません。 観測性を維持するには、 分散トレース と相関識別子を常に使用する必要があります。
レジリエンス ハンドラーの通信。 オーケストレーター主導の設計では、中央コンポーネントは、一時的、非一時的、タイムアウトエラーの再試行処理などの回復性の責任を専用の回復性ハンドラーに委任できます。
コレオグラフィベースの設計でオーケストレーターを削除しても、ダウンストリーム コンポーネントは回復性の責任を負いません。 レジリエンシーハンドラーで集中管理されたままになります。 しかし、ダウンストリーム コンポーネントはそのハンドラーと直接通信する必要があり、ポイントツーポイント通信が増加します。
イベント スキーマの進化。 イベント スキーマの進化により、時間の経過と同時にコンシューマーが破壊的変更を引き起こす可能性があります。 このパターンでは、複数の独立したサービスが同じイベントを使用します。 プロデューサーがイベントのデータ構造を変更すると、古いスキーマに依存するダウンストリーム コンシューマーが中断される可能性があります。 スキーマ レジストリを使用してイベント コントラクトを管理し、サービスが個別に進化するにつれて下位互換性のある進化を使用します。
べき等性とイベントの順序付け。 少なくとも 1 回の配信と再試行によって重複するメッセージが生成される可能性があり、同時コンシューマーはメッセージを順番に処理できません。 安定したメッセージ識別子を追跡することで、コンシューマーが 冪等 になるように設計します。 順序付けされた処理が必要な場合は、Service Bus セッションなどのブローカー機能を使用するか、コンシューマーが古いイベントを拒否してギャップを検出できるようにするシーケンスまたはバージョン データを含めます。
アトミック状態とイベント公開。 データ ストアを更新し、別の操作でイベントを発行するサービスは、1 つの操作をコミットできますが、もう 1 つの操作は失敗します。 トランザクション送信トレイ パターンまたは同等のアトミック メカニズムを使用して、別のプロセスがイベントを発行する前に状態の変更とイベントを一緒に保持します。
創発的な振る舞いとイベントストーム。 分散型イベント トポロジは、大規模な新しい動作を作成できます。 多くのサービスが互いのイベントに反応すると、システムが誤ってフィードバック ループまたはイベント ストームを生成する可能性があります。 マイナー イベントは、ダウンストリームの反応の連鎖を引き起こす可能性があります。 循環イベント チェーンを防ぐには、イベント フィルター処理、コンシューマーコンカレンシー制限、調整、明示的ルールなどのガードレールを使用します。
このパターンを使用する場合
このパターンは次の状況で使用します。
ダウンストリーム コンポーネントは、ファイア アンド フォーゲット方式でアトミックな操作を独立して処理します。 各コンポーネントはタスクを完了し、メッセージ ブローカーを介して他のコンポーネントに完了を通知します。 開始サービスでは、ディスパッチ後にタスクがアクティブに管理または追跡されることはありませんが、ダウンストリーム サービスは引き続きイベントを通じて結果を伝達します。
コンポーネントを頻繁に更新して置き換える必要があります。 このパターンを使用すると、既存のサービスに対する労力を減らし、中断を最小限に抑えてアプリケーションを変更できます。
単純なワークフローにはサーバーレス アーキテクチャを使用します。 コンポーネントは、有効期間が短く、イベントドリブンである場合があります。 イベントが発生すると、サービスはタスクを実行するコンポーネントを作成し、そのタスクの完了後にコンポーネントを削除します。
境界付けられたコンテキスト間の通信には、ドメイン境界を越えた疎結合が必要です。 単一の境界付けられたコンテキスト内の通信については、複雑さとチームの好みに応じて、オーケストレーター パターンを検討してください。
中央オーケストレーターでは、パフォーマンスのボトルネックが発生します。
このパターンは、次の場合に適さない場合があります。
アプリケーションは複雑で、ダウンストリーム コンポーネントを軽量に保つために、共有ロジックを処理するための中央コンポーネントが必要です。
コンポーネント間のポイントツーポイント通信は避けられません。
ビジネス ロジックを使用して、ダウンストリーム コンポーネントが処理するすべての操作を統合する必要があります。
ワークロード設計
ワークロードの設計でコレオグラフィ パターンを使用して、 Azure Well-Architected Framework の柱で説明されている目標と原則に対処する方法を評価します。 次の表は、このパターンが各柱の目標をサポートする方法に関するガイダンスを示しています。
| 支柱 | このパターンが柱の目標をサポートする方法 |
|---|---|
| オペレーショナルエクセレンス は、標準化されたプロセスとチームの結束によってワークロードの品質を提供します。 | このパターンの分散コンポーネントは自律的であり、置き換え可能に設計されているため、システムの全体的な変更を減らしながらワークロードを変更できます。 - OE:04 ツールとプロセス |
| パフォーマンス効率 は、スケーリング、データ、およびコードの最適化を通じて、ワークロード の需要を効率的に満たすのに役立ちます。 | このパターンは、中央集中型オーケストレーショントポロジでパフォーマンスボトルネックが発生した場合の代替方法を提供します。 - PE:02 容量計画 - PE:05 スケーリングとパーティショニング |
設計決定と同様に、このパターンで導入される可能性のある他の柱の目標とのトレードオフを考慮してください。
例
この例では、マイクロサービスと共に関数を実行するイベント ドリブンのクラウドネイティブ ワークロードを作成することで、コレオグラフィ パターンを示します。 クライアントがパッケージの発送を要求すると、ワークロードによってドローンが割り当てられます。 スケジュールされたドローンでパッケージを受け取る準備ができたら、配送プロセスが開始されます。 パッケージが輸送中、ワークロードは出荷状態を受け取るまで配信を処理します。 完全な参照アーキテクチャについては、「Azure Container Appsを使用したマイクロサービス」を参照してください。
インジェスト サービスはクライアント要求を受信し、配信の詳細を含むメッセージに変換します。 ビジネス トランザクションは、サービスがそれらの新しいメッセージを使用した後に開始されます。
1 つのクライアント ビジネス トランザクションには、次の 3 つの異なるビジネス操作が必要です。
パッケージを作成または更新します。
ドローンを割り当ててパッケージを配送します。
パッケージが出荷されたときに通知を確認および送信するなど、配信を処理します。
パッケージ、ドローン スケジューラ、および配信マイクロサービスは、ビジネス処理を実行します。 サービスは、中央オーケストレーターではなくメッセージングを使用して相互に通信します。 各サービスは、分散化された方法でビジネス ワークフローを調整するプロトコルを事前に実装する必要があります。
設計
サービスは、複数のホップを通じて一連のビジネス トランザクションを処理します。 各ホップは、すべてのビジネス サービス間で 1 つのメッセージ バスを共有します。
クライアントが HTTP エンドポイントを介して配信要求を送信すると、インジェスト サービスはそれを受信し、メッセージに変換して、共有メッセージ バスにメッセージを発行します。 サブスクライブされたビジネス サービスは、バスに追加された新しいメッセージを処理します。 ビジネス サービスがメッセージを受信すると、操作が正常に完了するか、要求が失敗またはタイムアウトします。要求が成功すると、サービスは Ok 状態コードを使用してバスに応答し、新しい操作メッセージを生成してメッセージ バスに送信します。 要求が失敗した場合、またはタイムアウトした場合、サービスはエラー理由コードをメッセージ バスに報告し、Azure Service Busを介してメッセージを配信不能にします。 このサービスは、一定時間内に受信または処理できないメッセージもデッドレターとして扱います。
この設計では、複数のメッセージ バスを使用してビジネス トランザクション全体を処理します。 Azure Service Bus と Azure Event Grid は 、この設計のためのメッセージング サービス プラットフォームを提供します。 ワークロードはAzure Container Appsで実行されます。 インジェスト サービスは Container Apps でホストされているAzure関数として実行され、パッケージ、ドローン スケジューラ、配信サービスは、同じ Container Apps 環境でマイクロサービスとして実行されます。 Container Apps は、ビジネス ロジックを実行する イベントドリブン処理 を処理します。
この設計により、振付が順番に行われることも保証されます。 1 つの Service Bus 名前空間には、2 つのサブスクリプションとセッション対応キューを持つトピックが含まれています。 インジェスト サービスは、トピックにメッセージを発行します。 パッケージ サービスとドローン スケジューラ サービスはトピックをサブスクライブし、成功した要求をキューに通知するメッセージを発行します。 配信サービスが各トランザクションに必要な 2 つのメッセージを関連付けることができるように、GUID を配信識別子に関連付ける共通セッション識別子を含めます。 1 つのメッセージはパッケージの準備ができていると確認し、もう 1 つのメッセージはドローンがスケジュールされていることを確認します。 このセッション ベースの関連付けがないと、中央コーディネーターがトランザクションの状態を追跡しないため、配信サービスは独立したホップ間で関連するメッセージを関連付けることはできません。 配信サービスは、トランザクションごとに 2 つの関連メッセージを待機します。 最初のメッセージは、パッケージを出荷する準備ができていることを示し、2 番目のメッセージはドローンがスケジュールされていることを通知します。
この設計では、Service Bus は、配信プロセス全体で失われたり重複したりしてはならない、価値の高いメッセージを処理します。 パッケージが出荷されると、状態の変更が Event Grid に発行されます。 イベントの送信者は、状態の変更がどのように処理されるかについて期待しません。 この設計に含まれていないダウンストリーム組織サービスは、このイベントの種類をリッスンし、注文状態の電子メールをユーザーに送信するなど、特定のビジネス ロジックを実行できます。
このパターンを AKS などの別のコンピューティング サービスにデプロイする場合は、ビジネス アプリケーションと同じポッドにサイドカーとしてアンバサダーをデプロイできます。 コロケーションにより通信の待ち時間は最小限に抑えられますが、プロキシによって処理とリソースのオーバーヘッドが増え、アプリケーションに合わせてスケーリングされます。 この方法は、プラットフォームで提供されない言語に依存しない接続の問題が必要な場合に使用します。
複数回試行される可能性がある連鎖的な再試行操作を回避するために、ビジネス サービスは、許容できないメッセージに直ちにフラグを設定する必要があります。 サービスが DLQ に移動できるように、一般的な理由コードまたは定義されたアプリケーション コードを使用して、これらのメッセージを強化します。 ダウンストリーム サービスからの整合性の問題を管理するために Saga パターンを実装することを検討してください。 たとえば、別のサービスは、補正、再試行、またはピボット トランザクションを実行することによってのみ、修復目的で配信不能メッセージを処理します。
ビジネスサービスは、冪等性を持ち、再試行操作で重複するリソースが作成されないようにします。 たとえば、パッケージ サービスはアップサート操作を使用してデータ ストアにデータを追加します。
次のステップ
- Azure の非同期メッセージング オプションを確認して、分散ワークフローを実装するために使用できるさまざまなインフラストラクチャの選択肢について学習します。
関連リソース
振付のためのデザインでは、次のパターンを検討してください。
アンバサダー パターンを使用して、メッセージ バスとのビジネス サービス通信をモジュール化します。
Queue-Based 負荷平準化パターンを実装して、ワークロードの急増を処理します。
Publisher-Subscriber パターンで非同期分散メッセージングを使用します。
補正トランザクションを使用して、1 つ以上の関連する操作が失敗した場合に一連の正常な操作を元に戻します。