gRPCのコアコンセプト、アーキテクチャ、およびライフサイクル
gRPCは、異なるマシン上のサーバーアプリケーションのメソッドを、ローカルオブジェクトのように直接呼び出すことができるリモートプロシージャコール(RPC)システムです。このガイドでは、gRPCの主要なコンセプト、アーキテクチャ、およびRPCライフサイクルについて説明します。
概要
サービス定義
多くのRPCシステムと同様に、gRPCはサービスを定義し、リモートで呼び出すことができるメソッドとそのパラメータおよび戻り値の型を指定するという考えに基づいています。デフォルトでは、gRPCはプロトコルバッファをインターフェース定義言語(IDL)として使用して、サービスインターフェースとペイロードメッセージの構造を記述します。必要に応じて他の代替手段を使用することも可能です。
以下は、サービス定義の例です
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
gRPCでは、以下の4種類のサービスメソッドを定義できます:
1.Unary RPC: クライアントがサーバーに単一のリクエストを送り、単一のレスポンスを受け取る通常の関数呼び出しのようなものです。
rpc SayHello(HelloRequest) returns (HelloResponse);
2.Server streaming RPC: クライアントがサーバーにリクエストを送り、サーバーからのメッセージのシーケンスを読み取るためのストリームを受け取ります。クライアントは、すべてのメッセージを読み取るまでストリームを読み続けます。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
3.Client streaming RPC: クライアントがメッセージのシーケンスを書き込み、サーバーに送信します。クライアントがメッセージの書き込みを終了すると、サーバーはそれらを読み取り、レスポンスを返します。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
4.Bidirectional streaming RPC: クライアントとサーバーの両方が読み書きストリームを使用してメッセージのシーケンスを送信します。2つのストリームは独立して動作するため、クライアントとサーバーは任意の順序でメッセージを読み書きできます。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
APIの使用
.proto
ファイルでサービス定義を行った後、gRPCはプロトコルバッファコンパイラプラグインを提供し、クライアントおよびサーバー側のコードを生成します。gRPCユーザーは通常、クライアント側でこれらのAPIを呼び出し、サーバー側で対応するAPIを実装します。
サーバー側: サーバーはサービスで宣言されたメソッドを実装し、クライアントからの呼び出しを処理するgRPCサーバーを実行します。gRPCインフラストラクチャは、受信リクエストをデコードし、サービスメソッドを実行し、サービスレスポンスをエンコードします。
クライアント側: クライアントは、サービスと同じメソッドを実装するローカルオブジェクト(スタブ)を持ちます。クライアントはこのローカルオブジェクトのメソッドを呼び出し、メソッドは呼び出しのパラメータを適切なプロトコルバッファメッセージタイプにラップし、リクエストをサーバーに送信し、サーバーのプロトコルバッファレスポンスを返します。
同期と非同期
同期RPC呼び出しは、サーバーからのレスポンスが到着するまでブロックされるため、RPCの抽象化に最も近いものです。一方、ネットワークは本質的に非同期であり、多くのシナリオでは現在のスレッドをブロックせずにRPCを開始できることが有用です。
gRPCのプログラミングAPIは、ほとんどの言語で同期および非同期の両方のバージョンを提供しています。詳細は各言語のチュートリアルおよびリファレンスドキュメントを参照してください。
RPCライフサイクル
このセクションでは、gRPCクライアントがgRPCサーバーメソッドを呼び出すときに何が起こるかを詳しく見ていきます。完全な実装の詳細については、言語固有のページを参照してください。
Unary RPC
最も単純なタイプのRPCで、クライアントが単一のリクエストを送り、単一のレスポンスを受け取るものです。
クライアントがスタブメソッドを呼び出すと、サーバーはこの呼び出しが行われたことを通知され、クライアントのメタデータ、メソッド名、および指定された期限が適用されます。
サーバーは、クライアントのリクエストメッセージを待つか、すぐに初期メタデータを返すことができます。どちらが先に行われるかはアプリケーション固有です。
サーバーがクライアントのリクエストメッセージを受け取ると、レスポンスを作成し、クライアントに返します。レスポンスには、ステータスの詳細(ステータスコードとオプションのステータスメッセージ)およびオプションのトレーリングメタデータが含まれます。
レスポンスステータスがOKであれば、クライアントはレスポンスを受け取り、呼び出しが完了します。
Server streaming RPC
サーバーストリーミングRPCは、Unary RPCに似ていますが、サーバーがクライアントのリクエストに対してメッセージのストリームを返す点が異なります。サーバーがすべてのメッセージを送信した後、ステータスの詳細とオプションのトレーリングメタデータがクライアントに送信されます。クライアントは、サーバーのすべてのメッセージを受け取ると完了します。
Client streaming RPC
クライアントストリーミングRPCは、Unary RPCに似ていますが、クライアントがサーバーに単一のメッセージではなくメッセージのストリームを送信する点が異なります。サーバーは、通常はクライアントのすべてのメッセージを受け取った後に単一のレスポンスを返します。
Bidirectional streaming RPC
双方向ストリーミングRPCでは、クライアントがメソッドを呼び出し、サーバーがクライアントのメタデータ、メソッド名、および期限を受け取ります。サーバーは初期メタデータを返すか、クライアントがメッセージのストリームを開始するのを待つことができます。
クライアントおよびサーバー側のストリーム処理はアプリケーション固有です。2つのストリームは独立しているため、クライアントとサーバーは任意の順序でメッセージを読み書きできます。例えば、サーバーはクライアントのすべてのメッセージを受け取るまで待つこともできますし、クライアントとサーバーが交互にメッセージを送受信することもできます。
デッドライン/タイムアウト
gRPCでは、クライアントがRPCの完了を待つ時間を指定できます。指定された時間内にRPCが完了しない場合、DEADLINE_EXCEEDEDエラーで終了します。サーバー側では、特定のRPCがタイムアウトしたかどうか、またはRPCを完了するまでの残り時間を確認できます。
デッドラインやタイムアウトの指定は言語固有です。一部の言語APIはタイムアウト(時間の長さ)で動作し、他の言語APIはデッドライン(固定された時点)で動作します。
RPCの終了
gRPCでは、クライアントとサーバーの両方が呼び出しの成功を独立して判断します。これにより、サーバー側で正常に終了したRPCがクライアント側で失敗することがあります。例えば、サーバーがすべてのレスポンスを送信した後にクライアントのデッドラインを超えた場合などです。
RPCのキャンセル
クライアントまたはサーバーのいずれかがいつでもRPCをキャンセルできます。キャンセルは即座にRPCを終了させ、以降の作業は行われません。
メタデータ
メタデータは、特定のRPC呼び出しに関する情報(認証情報など)をキーと値のペアのリストとして提供します。キーは文字列で、値は通常文字列ですが、バイナリデータも可能です。
キーは大文字小文字を区別せず、ASCII文字、数字、および特殊文字-、_、.を含むことができますが、grpc-で始まることはできません(これはgRPC自体のために予約されています)。バイナリ値のキーは-binで終わります。
ユーザー定義のメタデータはgRPCによって使用されず、クライアントが呼び出しに関連する情報をサーバーに提供したり、その逆を行ったりするために使用されます。
チャンネル
gRPCチャンネルは、指定されたホストとポートでgRPCサーバーへの接続を提供します。クライアントスタブを作成する際に使用されます。クライアントは、メッセージ圧縮のオン/オフなど、gRPCのデフォルトの動作を変更するためのチャンネル引数を指定できます。チャンネルには、connectedやidleなどの状態があります。
チャンネルの閉じ方は言語によって異なります。一部の言語では、チャンネルの状態をクエリすることもできます。