gRPCあれこれ
RPCとは
・Remote Procedure Call(遠隔手続き呼び出し)
あるサービスから別のサービスの
アプリケーションの処理(サブルーチン・クラス・関数など)を呼び出す技術。
RPCを使うことで、違うアプリケーションのロジックを
あたかも自分のアプリケーションの中に実装されているかのように扱うことができる。
RPCでよく使われる技術には、gRPC・JSON-RPC・SOAP、Apache Thriftなどがある。
gRPCとは
・Googleが開発した高速なAPI通信とスキーマ駆動開発を実現するRPCフレームワーク
REST
・RESTはリソース思考を強く打ち出している
リソース志向はリソース(オブジェクト)を中心に考え、
これに対してHTTPメソッドで操作していく考え。
RPCではメソッドの呼び出しが基点となり、
データはあくまでその副産物であるため考え方としては逆になる。
RESTは規格が厳格に決められたものではなく、
シンプルでスケーラブルなAPIを作るための設計原則。
そのため、RESTでは原則に沿って自分で仕様を決めて実装することが求められる。
一方でRPCフレームワークは、
規格や仕様に沿って実装されたライブラリやフレームワークとして提供される。
特徴
・HTTP/2による高速な通信
・Protocol Buffers
・柔軟なストリーミング形式
HTTP/2
gRPCではHTTP/2のプロトコル上で通信が行われる。
HTTP/2では通信時にデータがテキストではなく、
バイナリにシリアライズされて送られる。
そのため小さな容量で転送でき、
ネットワーク内のリソースをより効率的に使用することができる。
またHTTP/2では一つのコネクションで
複数のリクエスト・レスポンスをやりとりできる。
そのためgRPCでもコネクションは常時張られっぱなしの状態になる。
リクエストのたびに接続と切断を行う必要がなく、
またヘッダーを都度おくる必要がないためより効率的な通信になる。
Protocol Buffers
gRPCではProtocol Buffersのフォーマットにシリアライズしてデータをやり取りする。
Protocol BuffersもgRPCと同様にGoogleが開発。
一番の特徴は、.protoファイルというIDL(インタフェース記述言語)。
.protoファイルを書いて、コンパイラを実行すると
任意の言語のサーバ・クライアント用コードを自動生成する。
そのため自分でAPIインタフェースを実装したり、
シリアライズされたデータのエンコード・デコード処理を書く必要がない。
gRPCではスキーマが最初に書かれるため
.protoファイルを見ればAPIの仕様は常に明確。
柔軟なストリーミング形式
・シンプルなRPC
・サーバーストリーミングRPC
・クライアントストリーミングRPC
・双方向ストリーミングRPC
protoファイル
// バージョン syntax = "proto3"; // パッケージ定義 package myapp; // サービス定義 service AddressBookService { rpc Search (SearchRequest) returns (SearchResponse); } message SearchRequest { string name = 1; } message SearchResponse { Person person = 1; } message Person { int32 id = 1; string name = 2; string email = 3; // リスト(配列) repeated PhoneNumber phone_number = 4; // 列挙型(先頭は必ず0) enum PhoneType { UNKNOWN = 0; MOBILE = 1; HOME = 2; WORK = 3; } message PhoneNumber { string number = 1; PhoneType phone_type = 2; } }
※メッセージ型は各言語のコードに自動生成された際に、
構造体やクラスとして書き出される。
・マップ(連想配列)
key_typeにキーとなる型を、value_typeに値となる型を定義
map<key_type, value_type> map_field = N; // 文字列をキーとしたAddressBookを格納させたい場合 message Person { int32 id = 1; map<string, AddressBook> address_books = 2; }
・oneof
フィールドの先頭にoneofと付与することで、
複数の型の中からどれかひとつという定義を行える。
※oneofはrepeatedにすることができず、
oneofの中でもrepeatedを使うことはできない。
message GreetingCard { int32 id = 1; oneof message { string text = 2; Image image = 3; Video video = 4; } } message Photo { ... } message Video { ... }
・日時
google.protobuf.Durationは期間を表す型
import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; message Person { int32 id = 1; google.protobuf.Duration apart_duration = 2; google.protobuf.Timestamp create_time = 3; google.protobuf.Timestamp update_time = 4; }
※特に値を返す必要がない場合
import "google/protobuf/empty.proto"; Service AddressBookService { rpc Delete(DeleteRequest) google.protobuf.Empty }
ストリーミングRPCの場合のメソッド定義
// サーバストリーミングRPC rpc SearchHello (SearchRequest) returns (stream SearchResponse); // クライアントストリーミングRPC rpc SearchHello (stream SearchRequest) returns (SearchResponse); // 双方向ストリーミングRPC rpc SearchHello (stream SearchRequest) returns (stream SearchResponse);
proto作成
mkdir -p proto touch proto/hello.proto
syntax = "proto3"; package hello; option go_package = "./pb"; import "google/protobuf/timestamp.proto"; service Greeter { rpc SayHello (HelloRequest) returns (HelloResponse) {} } message Greet { enum GreetingType { UNKNOWN = 0; GOODMORNING = 1; HI = 2; HELLO = 3; } string name = 1; Greet greet = 2; float smile_score = 3; google.protobuf.Timestamp create_time = 15; } message Report { message GreetCount { Greet.GreetingType greet = 1; int32 count = 2; } repeated GreetCount greet_counts = 1; } message HelloRequest { Greet.GreetingType greet = 1; } message HelloResponse { Greet greet = 1; }
コード生成
必要なプラグインのインストール
mkdir -p proto/go
protoc --go_out=proto/go --go-grpc_out=proto/go proto/*.proto
grpc_cli ls localhost:3000 パッケージ名.サービス名 -l