RSS

HTTP/2上のgRPC:堅牢で高性能なプロトコルのエンジニアリング

以前の記事では、HTTP/2 がネットワーク効率を劇的に向上させ、長期間接続のフレームワークを提供することでリアルタイム通信を可能にする方法を検討しました。この記事では、gRPC が HTTP/2 の長期間接続を基盤として、サービス間通信のための高性能でロバストなプラットフォームをどのように構築しているかを見ていきます。gRPC と HTTP/2 の関係、gRPC が HTTP/2 接続をどのように管理するか、そして gRPC が HTTP/2 を使用して接続を維持、健全化、活用する方法を検討します。

gRPC セマンティクス

まず、gRPC の概念が HTTP/2 の概念とどのように関連しているかを理解しましょう。gRPC は、「チャネル」1、「リモートプロシージャコール」(RPC)、そして「メッセージ」という 3 つの新しい概念を導入しています。これら 3 つの関係は単純です。各チャネルは多くの RPC を持ち、各 RPC は多くのメッセージを持つことができます。

Channel mapping

gRPC セマンティクスが HTTP/2 とどのように関連しているかを見てみましょう。

gRPC on HTTP/2

チャネルは gRPC における重要な概念です。HTTP/2 のストリームは、単一の接続で複数の同時会話を可能にします。チャネルは、複数の同時接続を介して複数のストリームを可能にすることで、この概念を拡張します。表面上、チャネルはユーザーがメッセージを送信するための簡単なインターフェースを提供しますが、その裏側では、これらの接続を維持、健全化、活用するために驚くほどのエンジニアリングが行われています。

チャネルは、エンドポイントへの仮想接続を表します。実際には、複数の HTTP/2 接続によってバックアップされている可能性があります。RPC は接続に関連付けられています(この関連については後述)。RPC は実際にはプレーンな HTTP/2 ストリームです。メッセージは RPC に関連付けられ、HTTP/2 データフレームとして送信されます。より具体的に言うと、メッセージはデータフレームの上に「レイヤー化」されています。1 つのデータフレームには多くの gRPC メッセージが含まれる場合があります。また、gRPC メッセージが非常に大きい場合2、複数のデータフレームにまたがることもあります。

リゾルバーとロードバランサー

接続を維持、健全化、活用するために、gRPC は「名前リゾルバー」と「ロードバランサー」を主とする数多くのコンポーネントを利用します。リゾルバーは名前をアドレスに変換し、それからこれらのアドレスをロードバランサーに渡します。ロードバランサーは、これらのアドレスから接続を作成し、接続間で RPC をロードバランシングする責任を負います。

Resolvers and Load Balancers

Round Robin Load Balancer

たとえば、DNS リゾルバーはホスト名を 13 個の IP アドレスに解決し、その後 RoundRobin バランサーが 13 個の接続(各アドレスへの接続)を作成し、各接続間で RPC をラウンドロビンすることができます。より単純なバランサーは、最初のアドレスへの接続のみを作成する場合があります。あるいは、複数の接続を望むものの、ホスト名が 1 つのアドレスにしか解決されないことを知っているユーザーは、バランサーに各アドレスに対して 10 回接続を作成させることで、複数の接続が使用されることを保証できます。

リゾルバーとロードバランサーは、gRPC システムにおける小さくも重要な問題を解決します。この設計は意図的です。問題空間を少数の小さく離散した問題に減らすことで、ユーザーはカスタムコンポーネントを構築しやすくなります。これらのコンポーネントは、gRPC を各システムの個々のニーズに適合するように微調整するために使用できます。

接続管理

設定されると、gRPC は(リゾルバーとバランサーによって定義された)接続プールを健全、アクティブ、そして活用された状態に保ちます。

接続が失敗すると、ロードバランサーは最後に知っているアドレスのリスト3を使用して再接続を開始します。同時に、リゾルバーはホスト名のリストの再解決を試みます。これは多くのシナリオで役立ちます。たとえば、プロキシに到達できなくなった場合、リゾルバーにそのプロキシのアドレスを含まないようにアドレスのリストを更新させたいでしょう。別の例としては、DNS エントリは時間とともに変更される可能性があるため、アドレスのリストを定期的に更新する必要がある場合があります。このように、gRPC は長期的な回復力のために設計されています。

解決が完了すると、ロードバランサーは新しいアドレスについて通知を受けます。アドレスが変更された場合、ロードバランサーは新しいリストに存在しないアドレスへの接続をダウンさせたり、以前は存在しなかったアドレスへの接続を作成したりする場合があります。

接続失敗の特定

gRPC の接続管理の効果は、失敗した接続を識別する能力にかかっています。接続の失敗には、一般的に 2 種類あります。通信されるクリーンな失敗と、通信されないあまりクリーンではない失敗です。

クリーンで観察しやすい失敗について考えてみましょう。クリーンな失敗は、エンドポイントが意図的に接続を切断した場合に発生します。たとえば、エンドポイントが正常にシャットダウンしたか、タイマーが超過してエンドポイントが接続を閉じた場合などです。接続がクリーンに閉じられる場合、TCP セマンティクスで十分です。接続を閉じると、FIN ハンドシェイク が発生します。これにより HTTP/2 接続が終了し、gRPC 接続も終了します。gRPC はすぐに再接続を開始します(前述)。これは非常にクリーンで、追加の HTTP/2 または gRPC セマンティクスは必要ありません。

よりクリーンではないケースは、エンドポイントがクライアントに通知せずにクラッシュしたりハングしたりする場合です。この場合、TCP は接続が失敗したと見なされるまで最大 10 分間リトライする可能性があります。もちろん、10 分間接続が切断されていることを認識できないのは許容できません。gRPC は HTTP/2 セマンティクスを使用してこの問題を解決します。KeepAlive を使用して設定されている場合、gRPC は定期的に HTTP/2 PING フレーム を送信します。これらのフレームはフロー制御をバイパスし、接続がアクティブかどうかを確認するために使用されます。PING 応答が適時に返ってこない場合、gRPC は接続が失敗したと見なし、接続を閉じ、再接続を開始します(前述)。

このように、gRPC は接続プールを健全に保ち、HTTP/2 を使用して接続の健全性を定期的に確認します。これらの動作はすべてユーザーには透過的であり、メッセージのリダイレクトは自動的にオンザフライで行われます。ユーザーは、常に健全な接続プールがあるかのようにメッセージを送信するだけです。

接続の維持

前述のように、KeepAlive は貴重な利点を提供します。HTTP/2 PING を送信して接続の健全性を定期的にチェックし、接続がまだアクティブかどうかを確認します。しかし、もう 1 つ同様に有用な利点があります。それは、プロキシへのアクティビティ信号を送信することです。

クライアントがプロキシを介してサーバーにデータを送信している状況を考えてみましょう。クライアントとサーバーは、必要に応じてデータを送信しながら、接続を無期限にアクティブに保つことができます。一方、プロキシはリソースが非常に限られていることが多く、リソースを節約するためにアイドル状態の接続を強制終了することがあります。Google Cloud Platform (GCP) のロードバランサーは、10 分後 にアイドル状態の接続を切断し、Amazon Web Services Elastic Load Balancers (AWS ELBs) は 60 秒後 に切断します。

gRPC が接続上で HTTP/2 PING フレームを定期的に送信することで、非アイドル接続の認識が作成されます。前述のアイドルキルルールを使用しているエンドポイントは、これらの接続のキルをパスします。

ロバストで高性能なプロトコル

HTTP/2 は、長期間接続のリアルタイム通信ストリームの基盤を提供します。gRPC は、接続プーリング、ヘルスセマンティクス、データフレームと多重化の効率的な使用、および KeepAlive によって、この基盤の上に構築されています。

プロトコルを選択する開発者は、今日の要求だけでなく明日の要求も満たすプロトコルを選択する必要があります。回復力、パフォーマンス、長期間または短期間の通信、カスタマイズ性、あるいは単にプロトコルが異常に大量のトラフィックにスケーリングし、効率性を維持できることを知っているかどうかに関わらず、gRPC を選択することは彼らにとって有益です。gRPC と HTTP/2 をすぐに使い始めるには、gRPC の入門ガイド を参照してください。

脚注

  1. Go では、gRPC のチャネルは ClientConn と呼ばれます。これは、「チャネル」という言葉が言語固有の意味を持つためです。
  2. gRPC は、HTTP/2 のデフォルトの最大データフレームサイズである 16KB を使用します。16KB を超えるメッセージは複数のデータフレームにまたがる可能性がありますが、16KB 未満のメッセージは、他のメッセージと 1 つのデータフレームを共有する場合があります。
  3. これは RoundRobin バランサーの動作ですが、すべてのロードバランサーがこのように動作するわけではありませんし、動作する必要もありません。