最近剛好看到 gRPC 蠻好奇是什麼的,就來了解一下,並試著用 golang 去實現
What are protocol buffers?
Protocol buffers are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data
- 簡單的來說,就是類似像
JSON
,XML
的序列化結構資料格式,但是更小、更快,而且更簡潔
- 目前支援多種語言
Java
, Python
, Objective-C
, C++
, Go
, Ruby
, and C#
- 只需要定義一次資料結構,就能自動生成符合你程式語言的檔案,讓你能夠直接在你的程式上使用。
- 結構就是文件,不需額外撰寫 API 文件,
.proto
檔本身就是一種「文件」。
What are gRPC?
- Simple service definition - 透過
Protocol Buffers
定義 service,就是可以在 .proto
檔案就可以定義好 service 內容
- Works across languages and platforms - 自動生成支援客戶端的語言,由下圖解說就是,Server跟 Client 端都透過
protocol buffers
來做傳遞,但三個分別是 ruby
, java
, c++
- Start quickly and scale
- Bi-directional streaming and integrated authBi-directional
Quick Start
基本的開發步驟是定義 proto 文件, 定義請求 Request 和 響應 Response 的格式,然後定義一個服務 Service, Service可以包含多個方法。
gRPC requires Go 1.6
or higher
.
Install gRPC
1
| go get -u google.golang.org/grpc
|
Install Protocol Buffers v3
https://github.com/protocolbuffers/protobuf/releases
- 根據自己用的 platform,下載編譯好的檔案,並解壓縮
- 將
bin/protoc
binary file 放置在 PATH 底下( ex. $GOPATH/bin
)
install the protoc plugin for Go
1
| go get -u github.com/golang/protobuf/protoc-gen-go
|
Try it!
gRPC services are defined in a .proto
file, which is used to generate a corresponding .pb.g
file.
The .pb.go
file is generated by compiling the .proto
file using the protocol compiler: protoc.
下載 grpc
時會有一個範例
1
| cd $GOPATH/src/google.golang.org/grpc/examples/helloworld
|
體驗一下,先啟動 sever
1
| go run greeter_server/main.go
|
再跑 client 會發現 response 回來的資料
1
2
3
| go run greeter_client/main.go
// Greeting: Hello world
|
Example
建立資料夾
1
2
3
4
| .
├── client // gRPC 客戶端
├── pb // 擺放 Protobuf 文件
└── server // gRPC 伺服器
|
client
:gRPC client,用來和伺服器溝通的程式
pb
: 定義的 Protobuf 文件,也會放置轉化後的 Protobuf 程式
server
: gRPC server
proto
定義資料格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| // 撰寫格式是 Proto v3。
syntax = "proto3";
// 生成的程式在 Golang 中將會屬於 `pb` 套件。
package pb;
// Calculator 定義了一個計算用的服務。
service Calculator {
// Plus 會接收 CalcRequest 資料作加總,最終會回傳 CalcReply。
rpc Plus (CalcRequest) returns (CalcReply) {}
}
// CalcRequest 包含了兩個數字,將會傳送至計算服務並對兩個數字進行計算。
message CalcRequest {
// 後面的數字是 Protocol Buffers 編碼與解碼所會用到的編號,能夠移除其中一個欄位而不打亂整個資料結構的編碼與解碼(除非更改了數字編號)
int32 number_a = 1;
int32 number_b = 2;
}
// CalcReply 是計算結果,將會回傳給客戶端。
message CalcReply {
int32 result = 1;
}
|
透過 protoc
產生 .pb.go
1
| protoc --go_out=plugins=grpc:. *.proto
|
- Generated client and server code.
- Code for populating, serializing, and retrieving our
HelloRequest
and HelloReply
message types.
目前資料夾
1
2
3
4
5
6
7
8
| .
├── client
│ └── main.go
├── pb
│ ├── calc.pb.go
│ └── calc.proto
└── server
└── main.go
|
server
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
35
36
37
38
39
40
41
42
43
44
45
| // server/main.go
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
"github.com/mgleon08/demo_proto/pb"
)
const (
port = ":50051"
)
// server 建構體會實作 Calculator 的 gRPC 伺服器。
type server struct{}
// Plus 會將傳入的數字加總。
func (s *server) Plus(ctx context.Context, in *pb.CalcRequest) (*pb.CalcReply, error) {
// 計算傳入的數字。
result := in.NumberA + in.NumberB
// 包裝成 Protobuf 建構體並回傳。
return &pb.CalcReply{Result: result}, nil
}
func main() {
// 監聽指定埠口,這樣服務才能在該埠口執行。
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("無法監聽該 port: %v", err)
}
// 建立新 gRPC 伺服器並註冊 Calculator 服務。
s := grpc.NewServer()
// RegisterCalculatorServer 是透過 .proto 定義轉換而成
pb.RegisterCalculatorServer(s, &server{})
// 開始在指定埠口中服務。
if err := s.Serve(lis); err != nil {
log.Fatalf("無法提供服務: %v", err)
}
}
|
client
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
35
| // client/main.go
package main
import (
"context"
"log"
"google.golang.org/grpc"
"github.com/mgleon08/demo_proto/pb"
)
const (
address = "localhost:50051"
)
func main() {
// 建立連線到遠端 gRPC 伺服器。
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("無法連線: %v", err)
}
// 結束後要關閉
defer conn.Close()
// 建立新的 Calculator 客戶端,所以等一下就能夠使用 Calculator 的所有方法。
c := pb.NewCalculatorClient(conn)
// 傳送新請求到遠端 gRPC 伺服器 Calculator 中,並呼叫 Plus 函式,讓兩個數字相加。
r, err := c.Plus(context.Background(), &pb.CalcRequest{NumberA: 32, NumberB: 32})
if err != nil {
log.Fatalf("無法執行 plus: %v", err)
}
log.Printf("回傳結果: %d", r.Result)
}
|
啟動
1
2
3
4
5
| // 先啟動 server
go run ./server/main.go
// 再啟動 client,就會有 response
go run ./client/main.go
|
demo
go-grpc-demo
Reference: