神奇的 gRPC,讓你把 call service 當成一個 function call - 實作篇
神奇的 gRPC,讓你把 call service 當成一個 function call - 實作篇

神奇的 gRPC,讓你把 call service 當成一個 function call - 實作篇

Tags
Kubernetes
ithome 2020 ironman
Date
Sep 24, 2020
本文章同時發佈於:
文章為自己的經驗與夥伴整理的內容,設計沒有標準答案,如有可以改進的地方,請告訴我,我會盡我所能的修改,謝謝大家~

大家好,繼昨天DAY09的介紹後,gRPC 相信大家已經稍有概念,接下來將介紹四種 gRPC 的傳輸方式:
  • 單向傳輸(Unary): Client 單向,Server 單向
  • Server 單向串流(Streaming-Server): Client 單向,Server 串流
  • Client 單向串流(Streaming-Client): Client 串流,Server 單向
  • 雙向串流(Streaming-Bidirectional): Client 串流,Server 串流
會以一個 Golang Client 一個 Golang Server 來講解。
以下講解 code 全部都放在DAY10-Github
notion image

建立 Protobuf schema

DAY09所述,必須先建立好一個 Protobuf schema,之後我們就可以用此 schema 產生 Client 與 Server 的 code。
unary傳輸
syntax = "proto3"; package helloworld; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
streaming-server傳輸
syntax = "proto3"; package helloworld; service Greeter { rpc SayHello (HelloRequest) returns (stream HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
streaming-client傳輸
syntax = "proto3"; package helloworld; service Greeter { rpc SayHello (stream HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
streaming-bidirectional傳輸
syntax = "proto3"; package helloworld; service Greeter { rpc SayHello (stream HelloRequest) returns (stream HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
  • package: 為產出來 code 的 packge name。
  • message: 為 protobuf 的訊息,你可以想像是 Restful API 的 request 與 response,但是介面更為嚴謹。
  • service: gRPC 的方法由此定義,以rpc SayHello (stream HelloRequest) returns (HelloReply) {}來說,SayHello是方法名,HelloRequest為參數,HelloReply為 result,有無stream代表了是否要用串流還是單向。
設計好了之後就可以執行以下 script 產生出gen資料夾,裡面就有實際的 code 了。
notion image
$ build-proto.sh

實際運作

package main import ( "context" "log" "net" pb "unary/gen/grpc-gateway/gen" "google.golang.org/grpc" ) const ( port = ":50052" ) // SayHello implements helloworld.GreeterServer.SayHello func (s *Server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { log.Printf("Received: %v", in.GetName()) return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil } // Server ... type Server struct { pb.UnimplementedGreeterServer } func main() { lis, err := net.Listen("tcp", port) 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) } }
以 unary 來說,將要import pb "unary/gen/grpc-gateway/gen",並將pb.UnimplementedGreeterServer實作,最後透過grpc.NewServer()來創建 gRPC Server,並透過pb.RegisterGreeterServer(s, &Server{})來註冊實作,最後以s.Serve(lis)啟動 Server。
而四種傳輸方法的測試方式都是:
  1. go run server/main.go: 啟動 server
  1. go run client/main.go: 使用 client 來呼叫
notion image
都大同小異,實作細節可以看 code,我舉出比較要注意的地方,
srv pb.Greeter_SayHelloServer為 Streaming 的方式,在 client 端呼叫後:
  1. 如果是 Streaming-Client: Server 可以透過此參數以.Recv()的方式持續接收資料,Client 可以透過此參數以.Send()的方式持續傳送資料。
  1. 如果是 Streaming-Server: Server 可以透過此參數以.Send()的方式持續傳送資料,Client 可以透過此參數以.Recv()的方式持續傳送資料。
  1. 如果是 Streaming-Bidirectional: Server 可以透過此參數以.Send()的方式持續傳送資料與.Recv()的方式持續接收資料,Client 可以透過此參數以.Send()的方式持續傳送資料與.Recv()的方式持續接收資料。

參考