LoginSignup
6
6

More than 3 years have passed since last update.

gRPC: メソッドの呼び出しと HTTP/2 フレームの関係

Posted at

この記事について

この記事では gRPC の Go言語実装である grpc-go を元に、 gRPC のメソッド呼び出しが HTTP/2 を使ってどう実現されているかをまとめる。

  • 環境情報
    • Go 言語のバージョン : 1.14.4
    • grpc-go のバージョン : 1.29.1

はじめに

この記事では、以下のようなシンプルな protobuf の定義(*.protoファイル)を用いて確認をしている。

syntax = "proto3";
package helloworld;

service Greeter {
  // Unary RPC
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
  // Server streaming RPC
  rpc SayHello_SS (HelloRequest) returns (stream HelloResponse) {}
  // Client streaming RPC
  rpc SayHello_CS (stream HelloRequest) returns (HelloResponse) {}
  // Bidirectional streaming RPC
  rpc SayHello_BI (stream HelloRequest) returns (stream HelloResponse) {}
}
message HelloRequest {
  string name = 1;
}
message HelloResponse {
  string message = 1;
}

メソッド呼び出しと応答のフレーム

概要

クライアントのプログラム内で gRPC のメソッドが呼ばれると、そのメソッドの情報や引数、戻り値などが HTTP/2 フレームで送受信される。以下は Unary のメソッドを呼び出した場合の例。

gRPC.png

  • メソッドの情報
    • 呼び出されたメソッドの情報(サービス名、メソッド名など)は HTTP/2 の HEADERS フレームでサーバーに送られる
    • サーバーはこれによりどのメソッドが呼ばれたかを判断して必要な初期化処理などを行う
  • メソッドの引数
    • メソッドの引数は DATA フレームでサーバーに送られて、サーバー側に実装されたメソッドに渡される
  • ステータス
    • サーバー側に実装されたメソッド呼び出しの成否、失敗した場合のエラーメッセージなどを HEADERS フレームでクライアントへ返す
  • 戻り値
    • メソッドの戻り値を DATA フレームで返す

詳細

メソッドの情報

クライアントで呼び出したメソッドの情報は、以下のような HEADERS フレームでサーバーに送信される。

  • HTTP メソッド : POST
  • パス: /サービス名/メソッド名 (例: /helloworld.Greeter/SayHello)
  • その他の HTTP ヘッダー (content-type や gRPC 固有のヘッダーなど)

それぞれをもう少し詳しく見ると次のようになる。

HTTPメソッド
gRPC メソッドの種類(Unary, Server-Streaming, Client-Streaming, Bidirectional-Streaming) によらず、HTTPリクエストの POST が使われる

パス
protobuf(*.proto ファイル)での定義に従い /サービス名/メソッド名 という形式になる。サービス名は packageservice で指定された名前。(例) helloworld.Greeter

HTTP ヘッダー
標準的な HTTP ヘッダーだけでなく gRPC 固有のヘッダーも複数用いるが、以下によく使われるものだけ記載。

  • content-type
    • 必須のヘッダー
    • 値は application/grpc、または、その後ろに +; に続けてサブタイプを付けたもの
      • 例) application/grpc+protoapplication/grpc+json
      • サブタイプはペイロードのエンコーディング方式を表す
      • 省略された場合のデフォルト値は proto (protobuf の意味)
    • content-type が無い、または値が application/grpc で始まってない場合は、エラーになる

[メソッド情報を送信してる HEADERS フレームの例]
unary_header.PNG

メソッドの引数

メソッドの引数は DATA フレームのペイロードに乗せてサーバーに送られる。ペイロードは gRPC の「固定長ヘッダー」と「メッセージ」から構成される。

[メソッドの引数を送信してる DATA フレームの例]
unary_data_frame.PNG

以下にペイロードの構造を記載。

  • 固定長ヘッダー(5バイト) ※青色の部分
    • ペイロードを圧縮してるかどうかのフラグ (1バイト)
      • 0 は未圧縮の意味
    • メッセージ部分の長さ(4バイト)
      • この場合、メッセージは7バイト
  • メッセージ ※オレンジ色の部分

ステータス

サーバー側でのメソッドの呼び出し成否やエラーメッセージを HEADERS フレームで返す。

  • 呼び出し自体が成功すれば、そのメソッドがエラーを返したとしても HTTP のステータスコードは 200 がクライアントに送られる
  • メソッドが返したエラーメッセージ(error.Error())は grpc-message ヘッダーとしてクライアントに送られる
  • メソッド呼び出し自体が失敗した場合(例:クライアントから送られた content-type ヘッダーが application/grpc から始まってない) 200 以外が返る。この例では 415 になる。

戻り値

戻り値は DATA フレームで引数と同様のペイロード形式でクライアントに送られる。

gRPC ストリーミングの場合

Server-Streaming, Client-Streaming, Bidirectional-Streaming のいずれかのストリーミングの場合、メソッド情報は HEADERS フレームで送られるが、gRPC のストリームに対して Send したメッセージは全て DATA フレームで送られる。

HTTP/2 ストリームとの関係

Unary の場合

1回の Unary メソッドの呼び出し/引数/戻り値は、同一の HTTP/2 ストリームで送受信される。
続けて同じメソッドを呼んだとしても別のストリームが使われる。

以下は同じ Unary のメソッドを2回続けて呼んだ例。1回目(青)は Stream ID 1、2回目(赤)は Stream ID 3 を用いていることが分かる。

unary-3times.PNG

ストリーミングの場合

メソッド呼び出し(HEADERS フレーム)、および、その呼び出しで取得した同一の gRPC ストリームに Send するメッセージ(DATA フレーム)は、全て同じ HTTP/2 ストリームで送受信される。

以下は Server-Streaming の例。メソッド情報送信に使われた Stream ID 1 が、サーバーからのストリーミングデータ送信にずっと使われていることが分かる。

server-stream-3times.PNG

参考

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6