gRPC と Protocol Buffers — 辞書を共有すれば、名前は送らなくていい
サーバー同士がデータをやり取りするとき、よく使われるのは JSON です。人間が読めて便利ですが、毎回「id」「name」「price」と項目名まで書いて送るので、同じ形のデータを何万回も送ると無駄がかさみます。
Protocol Buffers(Protobuf)は、送る側と受ける側が同じ辞書(スキーマ)を持っておくことで、項目名を番号1つに置き換えてしまう方式です。翻訳こんにゃくを両方が食べていれば、長い説明は要らない。図1で1項目ずつ変換して、何バイトまで縮むかを見てください。
名前の代わりに番号、文字の代わりにバイト
Protobuf の変換規則は単純です。各項目は「タグ」+「値」の組で、タグは項目の番号と型を1バイトに詰めたものです。id = 1 で整数なら 08、name = 2 で文字列なら 12。受け取った側は辞書を引いて、「1番は id だな」と名前を復元します。
数値も文字にしません。JSON では 3000 が4文字=4バイトですが、Protobuf は7ビットずつに切って詰める(varint)ので2バイト。小さい数ほど短くなります。読みやすさを捨てて、機械にとっての短さと速さを取った形式なのです。
図1の最後で「辞書を持っていない」に切り替えると、受け取った側には番号と値しか分かりません。両者が同じ .proto を持っていることが、この会話の前提です。番号を変えずに項目を足していけば、古い辞書の相手とも話し続けられます。
gRPC — 遠くの関数を、手元の関数のように呼ぶ
gRPC は、この Protobuf を使って別のサーバーにある関数を呼び出す仕組みです。.proto に「GetItem という関数は、こういう引数を受け取り、こういう答えを返す」と書いておくと、各言語用の呼び出しコード(スタブ)が自動で生成されます。
呼ぶ側は URL を組み立てたり JSON を解析したりせず、ふつうの関数のように呼ぶだけ。変換と通信はスタブが引き受けます。図2の「呼び出す」を押して、1回の呼び出しがどこを通るかを追ってください。
client.GetItem({ id: 150 }) だけです。「呼び出す」を押すと、その1行の裏で何が起きるかを順に光らせます。
通信には HTTP/2 を使うので、1本の接続で何本もの呼び出しを並行して流せます。データが軽く、接続を使い回せるぶん、サーバー同士が大量にやり取りするマイクロサービスで特に好まれます。逆に、ブラウザから直接呼ぶのは苦手で、人間がのぞいて確かめるのも JSON より手間です。外向きの API は JSON、内側の配管は gRPC、という使い分けがよく見られます。
- Protocol Buffers
- スキーマで項目に番号を振り、バイト列として詰めて送るデータ形式。
- .proto
- メッセージの形と番号、呼び出せる関数を書いた定義ファイル。両者で共有する。
- シリアライズ
- データを送れる形(バイト列など)に変換すること。戻すのはデシリアライズ。
- varint
- 数値を7ビットずつに区切り、小さい数ほど短く表す書き方。
- gRPC
- Protobuf と HTTP/2 を使い、別のサーバーの関数を呼び出す仕組み。
- スタブ
- .proto から自動生成される呼び出しコード。変換と通信を代行する。
まとめ
JSON は毎回、項目名まで書いて送る手紙。Protobuf は辞書を共有しておき、番号とバイトだけ送る電報です。図1では 57 バイトが 19 バイトになりました。gRPC はその電報を HTTP/2 で運び、遠くの関数を手元の関数のように呼べるようにします。代わりに失うのは、人間がそのまま読める気軽さです。