gRPC の Go の QuickStart チュートリアルをやった時のメモです。
プロトコルバッファコンパイラのインストール
$ sudo apt install -y protobuf-compiler
protoc
コマンドが利用可能になります。
% protoc --version libprotoc 3.14.0
プロトコル バッファ コンパイラ(protoc コマンド) は C++ で記述されており、これだけだと C++ に対してしか使えません。 各言語向けのコードを生成するには, 各言語のプロトコルバッファコンパイラプラグインをインストールする必要があります。
Go の場合は以下です。
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
protoc コンパイラがプラグインを見つけられるように PATH を更新します。
$ export PATH="$PATH:$(go env GOPATH)/bin"
サンプルを触ってみる
helloworld/helloworld/helloworld.proto
を見てみます。
syntax = "proto3"; option go_package = "google.golang.org/grpc/examples/helloworld/helloworld"; option java_multiple_files = true; option java_package = "io.grpc.examples.helloworld"; option java_outer_classname = "HelloWorldProto"; package helloworld; // 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; }
Protocol Buffer v3の基本構文
ここでProtocol Buffer v3の構文を確認してみます。
Message 定義
Protocol Buffer v3 の Message の定義の基本形は以下のようです。
message Message型 {
型 フィールド名 = フィールド番号
型 フィールド名 = フィールド番号
:
}
Service 定義
gRPCではこの機能を使い、プロトコル定義ファイル内で定義されたサービスに対応するコードを生成するようになっている。サービスは次のように定義できる。
Message を RPC で使用する場合は、 .proto
ファイルで RPC Service Interface を定義することになり、これがあると、プロトコルバッファーコンパイラーによって、選択した言語でサービスインターフェースコードとスタブが生成されるようです。
* Language Guide (proto 3) | Protocol Buffers Documentation
service Service名 { rpc メソッド名(引数(リクエスト)となるMessage型型) returns (戻り値(レスポンス)のMessage型型); }
Protocol Buffer v3を見たのでチュートリアルに戻ります。
helloworld/helloworld.pb.go
に、プロトコルバッファーコンパイラー (protoc) によって生成されたと思われるコードもすでに配置されています。
チュートリアルにある通りにService定義にメソッドを追加してみます。
// The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} // Sends another greeting rpc SayHelloAgain (HelloRequest) returns (HelloReply) {} }
プロトコルバッファーコンパイラーでコンパイルして、gRPC コードを再生成します
% protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ helloworld/helloworld.proto
helloworld/helloworld.pb.go
は以下のような差分になりました。
SayHelloAgain()メソッドが追加され、インターフェースにも定義されています。
@@ -1,22 +1,8 @@ -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 -// - protoc v4.25.2 -// source: examples/helloworld/helloworld/helloworld.proto +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.14.0 +// source: helloworld/helloworld.proto package helloworld @@ -32,16 +18,13 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 -const ( - Greeter_SayHello_FullMethodName = "/helloworld.Greeter/SayHello" -) - // GreeterClient is the client API for Greeter service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type GreeterClient interface { // Sends a greeting SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) + SayHelloAgain(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) } type greeterClient struct { @@ -54,7 +37,16 @@ func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { out := new(HelloReply) - err := c.cc.Invoke(ctx, Greeter_SayHello_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *greeterClient) SayHelloAgain(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { + out := new(HelloReply) + err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHelloAgain", in, out, opts...) if err != nil { return nil, err } @@ -67,6 +59,7 @@ func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ... type GreeterServer interface { // Sends a greeting SayHello(context.Context, *HelloRequest) (*HelloReply, error) + SayHelloAgain(context.Context, *HelloRequest) (*HelloReply, error) mustEmbedUnimplementedGreeterServer() } @@ -77,6 +70,9 @@ type UnimplementedGreeterServer struct { func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) { return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") } +func (UnimplementedGreeterServer) SayHelloAgain(context.Context, *HelloRequest) (*HelloReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHelloAgain not implemented") +} func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {} // UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service. @@ -100,7 +96,7 @@ func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(in } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Greeter_SayHello_FullMethodName, + FullMethod: "/helloworld.Greeter/SayHello", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) @@ -108,6 +104,24 @@ func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _Greeter_SayHelloAgain_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServer).SayHelloAgain(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/helloworld.Greeter/SayHelloAgain", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServer).SayHelloAgain(ctx, req.(*HelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Greeter_ServiceDesc is the grpc.ServiceDesc for Greeter service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -119,7 +133,11 @@ var Greeter_ServiceDesc = grpc.ServiceDesc{ MethodName: "SayHello", Handler: _Greeter_SayHello_Handler, }, + { + MethodName: "SayHelloAgain", + Handler: _Greeter_SayHelloAgain_Handler, + }, }, Streams: []grpc.StreamDesc{}, - Metadata: "examples/helloworld/helloworld/helloworld.proto", + Metadata: "helloworld/helloworld.proto", }
greeter_server/main.go
を修正します。
func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: "Hello again " + in.GetName()}, nil }
greeter_client/main.go
を修正します。
r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: *name}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %s", r.GetMessage())
実行
% go run greeter_server/main.go
% go run greeter_client/main.go --name=Alice
Greeting: Hello Alice Greeting: Hello again Alice
まとめ
gRPC の Go の QuickStart チュートリアルをやった時のログを記載しました。
参考
- GitHub - protocolbuffers/protobuf: Protocol Buffers - Google's data interchange format
- gRPC Motivation and Design Principles | gRPC
- Quick start | Go | gRPC
- GitHub - protocolbuffers/protobuf: Protocol Buffers - Google's data interchange format
- サービス間通信のための新技術「gRPC」入門 | さくらのナレッジ
- gRPC サービスを構成する | Cloud Endpoints with gRPC | Google Cloud
- Language Guide (proto 3) | Protocol Buffers Documentation
- Language Guide (proto 3) | Protocol Buffers Documentation