在 Go 專案使用 gRPC 遠端程序呼叫

前言

gRPC(gRPC Remote Procedure Calls)是 Google 發起的一個開源遠端程序呼叫(Remote procedure call)系統。此系統基於 HTTP/2 協定傳輸,使用 Protocol Buffers 作為介面描述語言。

一個簡單的 Protocol Buffers 語法如下:

1
2
3
4
5
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
}

做法

建立專案。

1
2
3
mkdir grpc-go-example
cd grpc-go-example
go mod init

安裝套件

安裝 grpc 套件。

1
go get -u google.golang.org/grpc

安裝 protoc-gen-go 套件。

1
go get -u github.com/golang/protobuf/protoc-gen-go

定義服務

新增 hello.proto 檔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
syntax = "proto3";

option go_package = ".;hello";

service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
string greeting = 1;
}

message HelloResponse {
string reply = 1;
}

使用以下指令,生成 hello.pb.go 檔:

1
protoc --go_out=plugins=grpc:. hello.proto

實作服務端

server 資料夾新增 main.go 檔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
"context"
pb "github.com/memochou1993/grpc-go-example"
"google.golang.org/grpc"
"log"
"net"
)

// 需要被實作的服務
type service struct {
pb.UnimplementedHelloServiceServer
}

// 實作 SayHello 方法
func (s *service) SayHello(ctx context.Context, r *pb.HelloRequest) (*pb.HelloResponse, error) {
log.Printf("Request received: %s", r.GetGreeting())
return &pb.HelloResponse{Reply: "Hello, " + r.GetGreeting()}, nil
}

func main() {
addr := "127.0.0.1:8080"
ln, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalln(err.Error())
}

s := grpc.NewServer()
pb.RegisterHelloServiceServer(s, new(service))
if err := s.Serve(ln); err != nil {
log.Fatalln(err.Error())
}
}

使用終端機執行服務端程式:

1
go run server/main.go

實作客戶端

client 資料夾新增 main.go 檔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"context"
pb "github.com/memochou1993/grpc-go-example"
"google.golang.org/grpc"
"log"
"time"
)

func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

// 連線
addr := "127.0.0.1:8080"
conn, err := grpc.DialContext(ctx, addr, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalln(err.Error())
}
defer conn.Close()

c := pb.NewHelloServiceClient(conn)
// 執行 SayHello 方法
r, err := c.SayHello(ctx, &pb.HelloRequest{Greeting: "World!"})
if err != nil {
log.Fatalln(err.Error())
}
log.Printf("Response received: %s", r.GetReply())
}

使用終端機執行客戶端程式:

1
2
go run client/main.go
Response received: Hello, World!

程式碼