什麼是 gRPC#
- Google 推出的 RPC framework
- 採用 Google 制定的 protocol buffers 當作資料傳輸格式
- 有
proto
工具可以把你寫好的 proto file 直接生成程式碼 - 比 RESTful API 更快、更有效率
- 更多請參考官網: grpc / grpc.io
開始寫 gRPC 服務之前,要先安裝一些 protocol buffer 的工具
protocol buffer compiler#
以下是快速安裝的腳本:
1
2
3
4
5
6
7
8
9
10
| #!/bin/bash
VERSION="3.6.0"
PLATFORM="linux-x86_64"
FILENAME="protoc-$VERSION-$PLATFORM.zip"
curl -LO "https://github.com/google/protobuf/releases/download/v$VERSION/$FILENAME"
sudo unzip -u $FILENAME -d /usr/local bin/protoc include/*
rm $FILENAME
|
也可以直接去官網下載: https://github.com/google/protobuf/releases
測試一下沒有安裝成功
1
2
| $ protoc --version
libprotoc 3.6.0
|
plugin for genrate go code#
1
| $ go get -u github.com/golang/protobuf/protoc-gen-go
|
gRPC package for go#
1
| $ go get -u google.golang.org/grpc
|
開始寫 gRPC 服務#
要有一個完整的 gRPC 服務,首先要寫 proto file 定義好服務提供的方法跟傳輸的資料格式
再由工具產生 pb.go 程式碼,最後跟著 pb.go 實作 server 跟 client 程式了
1. 建立 proto 文件#
這裡簡單寫一個 echo service
1
| $ mkdir proto; vi proto/echo.proto
|
1
2
3
4
5
6
7
8
9
10
11
| syntax = "proto3";
package proto;
message EchoMessage {
string value = 1;
}
service EchoServer {
rpc Echo (EchoMessage) returns (EchoMessage) {}
}
|
更多的語法請參考: Language Guide (proto3)
2. 產生 go 程式碼#
借由 protoc 產生程式碼
1
| $ protoc --go_out=plugins=grpc:. proto/*.proto
|
會產生 echo.pb.go
裡面包括 server 端的 interface 跟 client 端的 程式
3. 建立 server 程式#
開始實作 EchoServer
import 剛剛產生的 echo.pb.go
1
2
3
| import (
pb "github.com/nyogjtrc/grpc-example/proto"
)
|
實作 Server 跟 方法:
1
2
3
4
5
6
7
8
| type echoServer struct{}
// Echo
func (s *echoServer) Echo(ctx context.Context, in *pb.EchoMessage) (*pb.EchoMessage, error) {
reply := new(pb.EchoMessage)
reply.Value = "echo:" + in.Value
return reply, nil
}
|
完整程式碼:
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
| package main
import (
"context"
"fmt"
"log"
"net"
pb "github.com/nyogjtrc/grpc-example/proto"
"google.golang.org/grpc"
)
type echoServer struct{}
// Echo
func (s *echoServer) Echo(ctx context.Context, in *pb.EchoMessage) (*pb.EchoMessage, error) {
reply := new(pb.EchoMessage)
reply.Value = "echo:" + in.Value
return reply, nil
}
func main() {
fmt.Println("grpc example echo server :8888")
lis, err := net.Listen("tcp", ":8888")
if err != nil {
log.Fatalf("can not listen %v", err)
}
var opts []grpc.ServerOption
s := grpc.NewServer(opts...)
pb.RegisterEchoServiceServer(s, &echoServer{})
s.Serve(lis)
}
|
4. 建立 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
36
| package main
import (
"bufio"
"context"
"fmt"
"log"
"os"
pb "github.com/nyogjtrc/grpc-example/proto"
"google.golang.org/grpc"
)
func main() {
var opts []grpc.DialOption
opts = append(opts, grpc.WithInsecure())
conn, err := grpc.Dial(":8888", opts...)
if err != nil {
log.Fatalf("fail to dial: %v", err)
}
defer conn.Close()
client := pb.NewEchoServiceClient(conn)
for {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter text: ")
text, _ := reader.ReadString('\n')
r, err := client.Echo(context.Background(), &pb.EchoMessage{Value: text})
if err != nil {
log.Fatalf("%v.Echo error: %v", client, err)
}
fmt.Println(r.Value)
}
}
|
5. 實測 gRPC server, client#
執行 server
1
2
| $ go run server/main.go
grpc example echo server :8888
|
執行 client
1
2
3
4
5
6
| $ go run client/main.go
Enter text: 123
echo:123
Enter text: hello
echo:hello
|
增加 gRPC gateway#
https://github.com/grpc-ecosystem/grpc-gateway
grpc-gateway 會建立一個 reverse proxy server 把 gRPC 服務轉換成 RESTful JSON API
轉成 RESTful API 之後,就可以使用 Postman 之類的工具測試寫好的服務了
1. 要安裝的 plugin#
1
| go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
|
2. proto 檔案增加 RESTful option#
就是把 RESTful 的 router 寫進來
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| syntax = "proto3";
package proto;
import "google/api/annotations.proto";
message EchoMessage {
string value = 1;
}
service EchoService {
rpc Echo (EchoMessage) returns (EchoMessage) {
option (google.api.http) = {
post: "/echo/echo"
body: "*"
};
}
}
|
3. 產生 pb.go, pb.gw.go#
1
2
3
4
5
6
7
8
| protoc -I/usr/local/include -I. \
-I$(GOPATH)/src \
-I$(GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=plugins=grpc:. proto/*.proto
protoc -I/usr/local/include -I. \
-I$(GOPATH)/src \
-I$(GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. proto/*.proto
|
4. 寫 reverse proxy 的進入點#
把 gRPC 服務加入 proxy 裡面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| func gatewayServer() {
fmt.Println("RESTful echo server :9999")
grpcAddr := "localhost:8888"
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := pb.RegisterEchoServiceHandlerFromEndpoint(ctx, mux, grpcAddr, opts)
if err != nil {
log.Fatalf("can not register endpoint %v", err)
}
http.ListenAndServe(":9999", mux)
}
|
5. 實測#
1
2
| $ curl -X POST http://localhost:9999/echo/echo -H 'Content-Type: application/json' -d '{ "value": "hi, how are you?" }'
{"value":"echo:hi, how are you?"}
|
- 這邊才剛開始摸 gRPC,對他底層的運作方式還不是很熟
- proto 檔案類似 router
- 要實作的 server interface 類似 controller
- 有 gateway 可以轉成 RESTful API 讓測試更方便了
- 一些 server 的設定還要花時間微調
Reference#