精読「マイクロサービスアーキテクチャ 第2版」(第Ⅰ部 基礎 - 第4章 マイクロサービスの通信スタイル)
マイクロサービスアーキテクチャ 第2版
マイクロサービスの設計、実装、運用に必要なベストプラクティスや最新技術を解説した、実践的なガイドブックです。これを読めば、マイクロサービスに関してそれっぽい会話もできますよ。
関連記事
プロセス内からプロセス間へ
パフォーマンス
プロセス内呼び出しは高速で最適化が可能[1]だが、プロセス間呼び出しはネットワーク通信が必要で大きなオーバーヘッドが発生します。通信回数やデータ量を最小限に抑え、効率的なAPI設計とデータ管理を心がけることが重要。
インタフェースの変更
プロセス内のインタフェース変更は簡単に統一対応できるが、マイクロサービス間では後方互換性を考慮し、段階的な移行やロックステップデプロイが必要
エラー処理
プロセス内ではエラー処理が単純化されるが、分散システムではタイムアウトやサービス停止など多様なエラーが発生します。HTTPのようなエラーレスポンスにセマンティクス[2]を持たせることで、クライアントが適切に補正(補償)対応を行い、システムの堅牢性が向上する
プロセス間通信のための技術:多数の選択肢
プロセス間通信に使用する技術は多岐にわたるが、適切な選択をするためには、まずどのような通信スタイル(同期、非同期、イベント駆動など)が必要かを考えることが重要。その上で、その通信スタイルに最も適した技術を選ぶべき
マイクロサービスの通信スタイル
うまく組み合わせる
実際、1つのマイクロサービスが複数の連携形式を実装していることが一般的
パターン:同期ブロッキング
マイクロサービスが別のサービスに呼び出しを送り、レスポンスを待つ間、処理が停止(ブロック) する
利点
- 直感的でシンプル: 順次実行されるコードに似ており、多くの開発者にとって馴染み深い
- 導入のしやすさ: 単一プロセスやモノリシックな設計からの移行時に、他の新しい考え方を導入するより負担が少ない
欠点
- 時間的結合の問題:呼び出し元と呼び出し先サービス間の結合が強くなる。
- システムの脆弱性:呼び出し先サービスの遅延や停止が、呼び出し元サービスに影響を与える
- 負荷の集中:呼び出し元がレスポンス待ちの間、システムリソースが効率的に利用されない
同期ブロッキングの用途
同期ブロッキング呼び出しは単純なマイクロサービスアーキテクチャでは問題ないが、呼び出しチェーンが長くなると問題が発生する
例えば、複数のマイクロサービス間で同期呼び出しを行う場合、1つでも問題が起きると全体の処理が失敗する可能性がある。また、多数のネットワーク接続が開かれたままで、リソース競合やネットワーク輻輳が発生するリスクも高まる
この問題を改善するために、バックグラウンドでの処理や呼び出しチェーンの短縮が有効。例えば、不正検知
サービスを主要な購入フローから外し、並列処理で対応する方法が有効
パターン:非同期非ブロッキング
ネットワーク越しの呼び出しが呼び出し元のマイクロサービスをブロックせず、他の処理を続けることができる。以下の3つの一般的な通信スタイルがある
-
共通データを介した通信
上流マイクロサービスが共通データを変更し、他のサービスがそのデータを利用する。 -
リクエスト/レスポンス
一方のマイクロサービスがリクエストを送信し、処理後に結果をレスポンスとして受け取る。 -
イベント駆動の対話
マイクロサービスがイベントをブロードキャストし、他のサービスがそれをリッスンして対応する。
利点
呼び出し元と受信側が時間的に分離され、呼び出し元が処理を待たずに他の作業を続けられること。長時間かかる処理でも効率的に進行でき、同期ブロッキング通信のように長時間待機する必要ない
欠点
複雑さと選択肢の多さ。様々な通信スタイルから選ぶ必要があり、その実装方法を調べると技術的な選択肢が膨大になる。非同期通信が自分のメンタルモデルに合わない場合、初めは困難が伴うこともある
非同期非ブロッキングの用途
どの形式を選ぶかを慎重に考える必要がある。各形式にはトレードオフが存在するが、一般的には、長時間実行されるプロセスや、再構築が難しい長い呼び出しチェーンがある場合に非同期通信が有効
パターン:共通データを介した通信
あるマイクロサービスが指定された場所にデータを配置し、別のマイクロサービスがそのデータを利用するパターン。この統合スタイルは基本的に非同期的で、プロセス間通信が間接的なため、目立たないことが多いが、非常に一般的な通信パターン
実装
永続データストア(ファイルシステムや分散メモリストア)を使用し、ポーリングで新しいデータを識別する。代表的な例はデータレイクとデータウェアハウスで、どちらも一方向のデータフローを前提にしている
利点
ファイルやデータベースの読み書きができれば簡単に実装でき、異なるシステム間の相互運用性も確保できる。大量データを一度に送信する場合にも有効
欠点
低遅延が必要な状況には向かない点。下流マイクロサービスはポーリングメカニズムを使って新しいデータを認識するため、リアルタイム処理が求められる場合には不向き。また、データストアの構造変更がマイクロサービス間の通信に影響を与える可能性があり、基盤となるデータストアの堅牢性に依存する。
共通データを介した通信の用途
使用する技術に制約がある場合や、古いシステムとの相互運用性を確保する場合に有効。[3]このパターンは、大量データの共有にも適しており、数ギガバイトのファイルをファイルシステムに送信したり、数百万行のデータをデータベースにロードする際に威力を発揮する。
パターン:リクエスト/レスポンス通信
マイクロサービス間でデータをやり取りする一般的な方法で、同期または非同期で行われる。
実装:同期と非同期
同期では、リクエスト後にレスポンスを待ちながら接続が維持され、非同期ではメッセージブローカーを通じてリクエストをキューに送信し、後で処理される。
非同期の場合、レスポンスを元のリクエストに関連付けるために状態をデータベースに保存する必要があり、タイムアウト処理も重要。
リクエスト/レスポンス通信の用途
処理結果が必要な場合や呼び出しの成否を把握したい場合に適しており、同期・非同期のどちらを選ぶかはトレードオフによる決定となる
パターン:イベント駆動通信
マイクロサービスがイベントを発行し、受信したサービスが独自に処理を行う非同期で疎結合な通信方式であり、責務を分散しシステムの柔軟性と自律性を高める
実装
イベント駆動通信では、イベント発行と受信にメッセージブローカーを使用することでスケーラビリティと耐障害性が向上するが、運用コストがかかり、要求に応じて最適な手段を選択することが重要
イベントの中身
-
IDのみ
通知
は顧客の識別子のみでは足らず、追加の情報(メールアドレスや顧客名) も必要。この場合、通知
は顧客
サービスから追加情報をリクエストする必要があるが、これがドメイン結合を強化し、パフォーマンスや運用面での課題を引き起こす可能性がある
-
完全な詳細イベント
理想的には、イベントに必要なすべての情報を含めて疎結合を維持すべきだが、情報量が多くなるとイベントのサイズが問題となる
よって、ハイブリッドなアプローチ(必要な情報だけをイベントに含め、残りは検索する)を採用することが推奨されている
イベント駆動通信の用途
他のサービスに指示を出す代わりに、下流のマイクロサービスに解決を任せるという魅力がある。疎結合を重視する場合、イベント駆動型の連携は有効だが、新たな複雑さを生む可能性もある。
注意深く進める
非同期およびイベント駆動アーキテクチャはスケーラビリティを提供する一方で、管理やエラー処理が複雑になり、慎重な監視と適切な再試行機能が必要
Discussion