RPC(Remote Procedure Call) 是一個電腦通訊協定,最早用在網路檔案系統上,透過該協定允許系統上的程式存取其它系統上的檔案。如今,如果你有接觸微服務,也常使用 RPC 來建立服務間的通訊。
以下說明所使用的環境
OS :Mac
Golang:1.14
Protocol Buffer:3
Editer:Visual Studio Code
Define
首先透過 Protocol Buffer 來定義 grpc service:
sample.proto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
syntax = "proto3";
option go_package = ".;rpc";
service SampleService {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
|
編譯 sample.proto 生成 sample.pb.go:
1
|
protoc -I proto --go_out=plugins=grpc:pkg/gRPC proto/sample.proto
|
在 sample.pb.go 最上方可看到版本的訊息,往下可以看到剛剛所定義 SampleService:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.12.3
// source: sample.proto
// ~~~~ 略
// SampleServiceServer is the server API for SampleService service.
type SampleServiceServer interface {
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}
// ~~~~ 略
|
這裡特別需注意 grpc,protobuf 和 protoc-gen-go 版本是否相容。
假設當中某一個的版本較高,可能會有找不到方法的問題 proto.Marshal: missing method ProtoReflect。
Server
接著透過 sample.pb.go 來實作 grpc server,這裡定義一個 server 來繼承 SampleService 接口,並實作 SayHello 的內容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// ~~~~ 略
func main() {
lis, err := net.Listen("tcp", ":6424")
if err != nil {
fmt.Println(err)
}
s := grpc.NewServer()
fmt.Println("grpc sample service ...")
rpc.RegisterSampleServiceServer(s, &server{})
if err := s.Serve(lis); err != nil {
fmt.Println(err)
}
}
type server struct {
rpc.SampleServiceServer
}
func (s *server) SayHello(ctx context.Context, in *rpc.HelloRequest) (*rpc.HelloReply, error) {
return &rpc.HelloReply{Message: "Hello " + in.GetName()}, nil
}
|
Client
client 端也是使用相同的 protocol buffer 來呼叫 SayHello:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
func main() {
conn, err := grpc.Dial("0.0.0.0:6424", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
fmt.Println(err)
}
defer conn.Close()
c := rpc.NewSampleServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &rpc.HelloRequest{Name: "redd"})
if err != nil {
fmt.Println(err)
}
fmt.Println(r.GetMessage())
}
|
Reference
gRPC
protobuf