CentOS 8 + Python の場合の例を示します。インストールにはそこそこの時間がかかります。
# CentOS 8 + Python
# yum -y install gcc-c++ python3 python3-devel
# pip3 install grpcio==1.39.0 grpcio-tools==1.39.0
プロトコルバッファを定義するプロトファイル(hello.proto)を作成します。
syntax = "proto3"; package hello; service HelloWorld { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
protoc を用いてスタブを作成します。
# python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./hello.proto
下記のファイルが作成されます。
hello.proto hello_pb2.py hello_pb2_grpc.py
.proto から生成されたサービサーを用いて、サーバプログラム(server.py)を作成します。
from concurrent import futures import grpc import hello_pb2 import hello_pb2_grpc class HelloWorld(hello_pb2_grpc.HelloWorldServicer): def SayHello(self, request, context): print("RECV: %s" % request.name) message = "Hello, %s!" % request.name print("SEND: %s" % message) return hello_pb2.HelloReply(message=message) def serve(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) hello_pb2_grpc.add_HelloWorldServicer_to_server(HelloWorld(), server) server.add_insecure_port('[::]:8000') server.start() server.wait_for_termination() if __name__ == '__main__': serve()
.proto から生成されたスタブを用いて、クライアントプログラム(client.py)を作成します。
import grpc import hello_pb2 import hello_pb2_grpc def run(): with grpc.insecure_channel('localhost:8000') as channel: stub = hello_pb2_grpc.HelloWorldStub(channel) response = stub.SayHello(hello_pb2.HelloRequest(name='Yamada')) print("RECV: %s" % response.message) if __name__ == '__main__': run()
まずはサーバを起動します。
# python3 ./server.py
次にクライアントを起動します。
# python3 ./client.py Hello, Yamada!
サーバから Hello, Yamada! というメッセージが返却されれば成功です。
Go言語の場合のサンプルを示します。Go 1.15, protoc 3.17, gRPC 1.39
必要なパッケージをインストールしておきます。
# dnf -y install golang wget unzip # go version go version go1.15.13 linux/amd64
protoc コマンドをインストールします。
# mkdir ~/protoc # cd ~/protoc # wget https://github.com/protocolbuffers/protobuf/releases/download/v3.17.3/protoc-3.17.3-linux-x86_64.zip # unzip ./protoc-3.17.3-linux-x86_64.zip # ~/protoc/bin/protoc --version libprotoc 3.17.3
Go 1.12 からは GOPATH モードではなく、モジュールモードが推奨されるそうなので、モジュールモードにして、必要なモジュールをダウンロードします。
# export GO111MODULE=on # export PATH="$PATH:$(go env GOPATH)/bin" # go get google.golang.org/protobuf/cmd/protoc-gen-go@v1.27 # go get google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
サンプルとなる hello モジュールを作成します。
# mkdir ~/hello # cd ~/hello # go mod init example.com/my-name/hello
プロトファイルを作成してコンパイルします。
# mkdir ./proto # vi ./proto/hello.proto
syntax = "proto3"; option go_package = "example.com/my-name/hello"; package hello; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
# ~/protoc/bin/protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. \ --go-grpc_opt=paths=source_relative ./proto/hello.proto
サーバプログラムを作成します。
# mkdir ./server # vi ./server/main.go
package main import ( "context" "log" "net" "google.golang.org/grpc" pb "example.com/my-name/hello/proto" ) type server struct { pb.UnimplementedGreeterServer } func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { log.Printf("RECV: %v", in.GetName()) message := "Hello, " + in.GetName() + "!" log.Printf("SEND: %v", message) return &pb.HelloReply{Message: message}, nil } func main() { lis, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }
クライアントプログラムを作成します。
# mkdir ./client # vi ./client/main.go
package main import ( "context" "log" "time" "google.golang.org/grpc" pb "example.com/my-name/hello/proto" ) func main() { conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := pb.NewGreeterClient(conn) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "Yamada"}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("RECV: %s", r.GetMessage()) }
ターミナルを二つ起動し、サーバプログラムと、クライアントプログラムを実行します。いくつかのパッケージが自動インストールされます。Go 1.16 からは自動インストールはされなくなるので、いくつか事前にインストールしておく必要があるかもしれません。
# go run ./server/main.go go: finding module for package google.golang.org/grpc go: finding module for package google.golang.org/grpc/status go: finding module for package google.golang.org/protobuf/reflect/protoreflect go: finding module for package google.golang.org/protobuf/runtime/protoimpl go: finding module for package google.golang.org/grpc/codes go: downloading google.golang.org/grpc v1.39.0 go: found google.golang.org/grpc in google.golang.org/grpc v1.39.0 go: found google.golang.org/grpc/codes in google.golang.org/grpc v1.39.0 go: found google.golang.org/grpc/status in google.golang.org/grpc v1.39.0 go: found google.golang.org/protobuf/reflect/protoreflect in google.golang.org/protobuf v1.27.1 go: found google.golang.org/protobuf/runtime/protoimpl in google.golang.org/protobuf v1.27.1 go: downloading github.com/golang/protobuf v1.5.0 go: downloading golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd go: downloading google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 go: downloading golang.org/x/net v0.0.0-20200822124328-c89045814202 go: downloading golang.org/x/text v0.3.0 2021/07/25 00:54:56 RECV: Yamada 2021/07/25 00:54:56 SEND: Hello, Yamada!
# go run ./client/main.go 2021/07/25 00:54:56 RECV: Hello, Yamada!