ネットワーク基礎:TCP/IP・HTTP・DNS・CDN
gRPC と HTTP/2 利用
この回でやること
Protocol Buffers と HTTP/2 を使う gRPC の特徴を学びます。
- 読む 約 10 分
- 最後にクイズ 1 問
「pages は数値ですか、文字列ですか」を毎回聞いている
JSON は、項目の名前と値を文字で書いて、データをやりとりする形式です。型とは値の種類のことで、数値と文字列ではプログラムでの扱いが違います。たとえば、ページ数を数値で送ると次の形です。
JSON
{"pages": 120}同じ項目でも、値を引用符で囲むと文字列になります。
JSON
{"pages": "120"}見た目は同じ 120 でも、数値ならそのまま足し算に使え、文字列なら数値に変換する手順が要ります。型の契約とは、pages は数値として渡す、と両側で決めてコードにも反映する約束です。コンパイルは、書いたコードを実行できる形に変換する作業です。型を検査する言語では、数値を受け取る生成コードに文字列の "120" を渡そうとすると、この作業の途中で食い違いが見つかり、実行前に止まります。
社内でサービスを分けて作っていると、隣のチームの API を呼ぶ場面が増えます。そのたびに、項目の名前と型を確認する会話が発生します。ドキュメントはあっても、実装が変わったときに一緒に直るとは限りません。
JSON でやりとりする限り、型は約束ではなく慣習です。片方が数値を文字列に変えても、呼ぶ側は動かしてみるまで気づけません。
先に契約を書いて、両側のコードを機械に作らせる
gRPC は、この順番を逆にします。まず .proto というファイル (Protocol Buffers、略して Protobuf という記法で書きます) に、やりとりする項目と呼べる操作を書きます。
proto
syntax = "proto3";
message Book {
string isbn = 1;
string title = 2;
int32 pages = 3;
}
service BookService {
rpc GetBook (GetBookRequest) returns (Book);
}
message GetBookRequest {
string isbn = 1;
}このファイルから、呼ぶ側と受ける側のコードを自動生成します。呼ぶ側は生成された関数を普通に呼ぶだけで、URL も本文の組み立ても出てきません。項目の名前や型を間違えれば、動かす前にコンパイルで止まります。
各項目に付いている 1 や 2 は、その項目の背番号です。転送するときは名前ではなく番号だけを送るので、title のような文字列が毎回流れません。JSON より小さく、読み書きも速くなります。
この番号は、一度使ったら二度と使い回しません。項目をやめるときも、番号は空けたまま残します。番号を別の型の項目に付け直すと、古い版のまま動いているサービスはその値を読み飛ばし、値が失われます。同じ型の別の項目 (たとえば publisher) に付け直せば、今度は古い版がその値を昔の意味で読んでしまいます。やめた番号は reserved に書いておくと、うっかり使い回せなくなります。名前を変えるのは自由ですが、番号は変えられない、と覚えておきます。
1 回の呼び出しで、何回でも返せる
gRPC は HTTP/2 の上で動きます。1 本の接続に複数の通り道を作れる性質をそのまま使うので、呼び出しの形が 4 種類あります。
| 形 | 送る | 返る | 使いどころ |
|---|---|---|---|
| 単発 | 1 回 | 1 回 | 普通の呼び出し |
| サーバー側連続 | 1 回 | 何回でも | 進捗や更新を流し続ける |
| クライアント側連続 | 何回でも | 1 回 | 大きなデータを分けて送る |
| 双方向 | 何回でも | 何回でも | 送りながら受け取る |
これらが 1 本の接続の中で同時に動きます。呼び出しごとに接続を張り直さないので、サービス同士が細かく呼び合う構成でも積み上がりが小さく済みます。
ブラウザからは、そのままでは呼べない
便利そうに見えますが、Web の画面から直接は使えません。ブラウザの JavaScript には、gRPC が必要とする低い層の操作が公開されていないからです。
回避策として、gRPC-Web という方式で、間にプロキシを 1 枚挟んで変換する方法が用意されています。ただし双方向の連続送受信は使えないなどの制約が残ります。
そのため実際には、外に公開する API は REST のまま、内部のサービス同士だけ gRPC、という分け方が多くなります。呼ぶ相手が自分たちの管理下にあり、生成したコードを両側に配れる場所でこそ、この仕組みは効きます。