codec
gRPC は非常に柔軟に設計されていて、例えば RPC でメッセージをやりとりする際に、どういうシリアライザでエンコードするかを自由に選ぶことができます。gRPC の公式ドキュメントではシリアライザとして Protocol Buffers のみが利用されていますし、デフォルトのシリアライザとなっているのであまりここを意識することはないかもしれません。
このシリアライザ機構は、codec
として抽象化されています。Go の場合、google.golang.org/grpc/encoding
以下に Codec
というインターフェースが定義されています。
type Codec interface { // Marshal returns the wire format of v. Marshal(v interface{}) ([]byte, error) // Unmarshal parses the wire format into v. Unmarshal(data []byte, v interface{}) error // Name returns the name of the Codec implementation. The returned string // will be used as part of content type in transmission. The result must be // static; the result cannot change between calls. Name() string }
各 Codec
の実装は、encoding
パッケージの RegisterCodec
を利用して codec を登録します。*1
より正確で詳細なドキュメントは grpc-go/Documentation/encoding.md に置かれています。
IDL
gRPC では IDL を利用してインターフェース定義・コード生成を行います。IDL は Interface Definition Language (インターフェース定義言語) の略称で、こちらもほとんどの場合 Protocol Buffers が使われます。*2
例えば、以下のようにどういうサービスがあり、どのような RPC が属しているか、そのリクエストとレスポンスの型は何か、といった情報を独自の記法で記述します。 *3
// The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; }
普段 gRPC を触っている方であれば見慣れているかと思います。
この定義を元に protoc-gen-*
を使い、クライアントとサーバのインターフェース部分のコードを自動生成することになります。
IDL と codec
普段は Protocol Buffers を使用しているため、IDL と codec という概念を意識することはないかもしれません。しかし、上に示したように、これらは明確に分離されていてそれぞれ別の実装を使用することが可能です。 例えば、IDL として Protocol Buffers を使い、codec として JSON を使うといったことができます。実際の例は以下の記事が非常に参考になります。
また、IDL を FlatBuffers 等に変更することもできますが、Protocol Buffers より操作が複雑であるため、codec に FlatBuffers を使いたい場合以外であまり使う理由はないでしょう。
当然 IDL を用意せずにパワーでクライアント・サーバを実装することもできますが、自動生成を使わないということは gRPC のメリットをすべて捨てていることと等価なのでやめたほうが良いでしょう。