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