LoginSignup
8
0

More than 1 year has passed since last update.

Go/gRPCコード生成の変更について

Last updated at Posted at 2021-12-30

結構時間が経っているのですが、Go/gRPCでのサーバー開発において、2020年にコード生成の方法が変更されました。
最近gRPCを利用し始めた方は特に気にされる必要はありませんが、以前からGoでgRPCサーバーを構築していて
生成プラグインを入れ替えてないけどそろそろ更新しとくかーといった際に役立ちそうな情報として記事にしておきます。

以前の生成方法

GoでgRPCサーバーのコード生成をする際、
元はprotocプラグインとしてgolang/protobufを使用していました。

この際にはこのようなコマンドでコード生成を行なっていました。

$ protoc -I . \
    --go_out=plugins=grpc:.
    --go_opt=paths=source_relative \
    ./helloworld.proto 

このコードから生成される<Service>.pb.goに必要な全てのコードが入っていました。

protocプラグインの変更

2020年3月より、まず
protocolbuffers/protobuf-goのv1.20.0のリリースとしてGoのシリアライズコード生成プラグイン(protoc-gen-go)が独立しました。

更に2020年10月にgrpc/grpc-gocmd/protoc-gen-go v1.0.0でサーバースタブ生成プラグイン(protoc-gen-go-grpc)が独立しました。

そのため現在、.protファイルからgRPCサーバースタブを生成する際には下記のようなコマンドを打つことになります。

$ protoc -I . \
    --go_out . \
    --go_opt paths=source_relative \
    --go-grpc_out . \
    --go-grpc_opt paths=source_relative \
    ./helloworld.proto

以前1ファイルにまとめられていたコードも<Service>.pb.go<Service>_grpc.pb.goに分かれて生成されます。

下位互換性の無い変更

また、これに伴い以前のprotoc-gen-goで生成したスタブとの間に下位互換性を崩す変更が入っています。

gRPCスタブを生成するとサービス実装用の<Service>Serverインターフェイスが生成され、
またそのインターフェイスの前方互換性の為のUnimplemented<Service>Server構造体が生成されます。

type GreeterServer interface {
    SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}

type UnimplementedGreeterServer struct {
}

func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {
    return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}

これは<Service>Serverインターフェイスの実装構造体にUnimplemented<Service>Serverをembedすることで
実際にインターフェイスのメソッドを満たさなくても実装可能とする為の仕組みですが、全てのメソッドの実装後にembedを外してしまうと、サービスを追加した際などにエラーとなってしまいます。

こうした事例が多かったからなのか、
protoc-gen-go-grpcではmustEmbedUnimplemented<Service>Serverのようなプライベートメソッドがインターフェイスに追加されており、
これによりUnimplemented<Service>Serverのembedが強制されるようになりました。

type GreeterServer interface {
    SayHello(context.Context, *HelloRequest) (*HelloReply, error)
    mustEmbedUnimplementedGreeterServer()
}

func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {}

実際のコード

このため、Unimplemented<Service>Serverを外すような実装をしている場合、protoc-gen-go-grpcでのコード生成に切り替えた途端にインターフェイス実装が崩れてしまうことになります。

この問題を回避する方法も用意されており、protoc-gen-go-grpcのReadmeに記載されています。
protoc-gen-go-grpcのオプションとしてrequire_unimplemented_servers=falseを指定することで
mustEmbedUnimplemented<Service>Serverを生成しない下位互換性を維持したコード生成が可能になっています。

$ protoc -I . \
    --go_out . \
    --go_opt paths=source_relative \
    --go-grpc_out . \
    --go-grpc_opt paths=source_relative \
    --go-grpc_opt require_unimplemented_servers=false \
    ./helloworld.proto
8
0
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
8
0