BACK
Featured image of post gRPC 基本介紹

gRPC 基本介紹

gRPC 是 Google 推出的 RPC 架構,其輕量、高效能以及全雙工等特性,非常適合用來開發需要即時傳遞資訊的服務。

參考網站 - gRPC 說明影片 @ BESG


SOURCE CODE
對應的程式碼可檢視 besg-grpc 的 repository。

gRPC 是什麼:以 Golang 說明與實作

說明

RPC 的全名是 remote procedure call,主要是作為電腦和電腦間溝通使用。A 電腦可以呼叫 B 電腦執行某些程式,B 電腦會將結果回傳給 A 電腦,A 電腦在收到回應後會再繼續處理其他任務。RPC 的好處在於,雖然 A 電腦是發送請求去請 B 電腦做事,但其呼叫的方式,就很像是 A 電腦直接在呼叫自己內部的函式一般。

gRPC 也是基於這樣的概念,讓想要呼叫 server 處理請求的 client,在使用這支 API 時就好像是呼叫自己內部的函式一樣簡單自然。從功能面來說,gRPC 就像 Web 常用的 Restful API 一樣,都是在處理請求和回應,並且進行資料交換,但 gRPC 還多了其他的功能和特色。

gRPC 是由 Google 開發的開源框架,它快速有效、奠基在 HTTP/2 上提供低延遲(low latency),支援串流,更容易做到權限驗證(authentication)。在下面的文章中,將會對於 gRPC 能提供的特色有更多說明。


Protocol Buffers 是什麼

在學習 gRPC 時,需要同時了解什麼是 Protocol Buffers。在傳統的 Restful API 中,最常使用的資料交換格式通常是 JSON;但到了 gRPC 中,資料交換的格式則是使用名為 Protocol Buffers 的規範/語言。

json
1
2
3
4
5
{
    "firstName": "Sushiro",
    "lastName": "Global",
    "age": 6
}
protocol buffers
1
2
3
4
5
6
// Protocol Buffers
message User {
    string first_name = 1;
    string last_name = 2;
    int32 age = 3;
}

也就是說,當我們想要使用 gRPC 的服務來交換資料前,必須先把資料「格式」和「方法」都定義清楚。

TIP:
使用 gRPC 前,不只需要先把資料交換的格式定義清楚,同時也需要把資料交換的方法定義清楚。

這裡要稍微釐清一點很重要的是,Protocol Buffers 可以獨立使用,不一定要搭配 gRPC;但使用 gRPC 一定要搭配 Protocol Buffers。


實作將 Protocol Buffers 編譯成在 Golang 中可使用的檔案

對應的程式碼可檢視 besg-grpc repository 中的 proto 資料夾。

STEP 1:撰寫 Protocol Buffers 檔案

  • 使用 message 定義資料交換的格式。
  • 使用 service 定義呼叫 API 的方法名稱。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
syntax = "proto3";  // 定義要使用的 protocol buffer 版本

package calculator;  // for name space
option go_package = "proto/calculator";  // generated code 的 full Go import path

message CalculatorRequest {
  int64 a = 1;
  int64 b = 2;
}

message CalculatorResponse {
  int64 result = 1;
}

service CalculatorService {
  rpc Sum(CalculatorRequest) returns (CalculatorResponse) {};
}

STEP 2:安裝編譯 Protocol Buffer 所需的套件

此部份可參考 編譯 Protocol Buffers(Compiling) 段落。

安裝 compiler (macOS)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 安裝 compiler,安裝完後就會有 protoc CLI 工具
brew install protobuf
protoc --version  # Ensure compiler version is 3+

# --- 使用 golang 才需要安裝以下兩個套件 ---

# 安裝 protoc-gen-go 後可以將 proto buffer 編譯成 Golang 可使用的檔案
go get github.com/golang/protobuf/protoc-gen-go

# 安裝 grpc-go 後,可以在 Golang 中使用 gRPC
go get -u google.golang.org/grpc

STEP 3:編譯 Protocol Buffer 檔案

進到放有 .proto 檔的資料夾後,在終端機輸入下述指令:

1
protoc *.proto --go_out=plugins=grpc:. --go_opt=paths=source_relative

在成功編譯好後,應該會看到同樣的資料夾位置出現 *.pb.go 的檔案,這就是編譯好後可以在 Golang 中使用 Protocol Buffer 和 gRPC 的檔案。


實作 gRPC Server

對應的程式碼可檢視 besg-grpc repository 中的 server 資料夾。

STEP 1:建立 gRPC server

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
type Server struct {}

func main() {
 fmt.Println("starting gRPC server...")

 lis, err := net.Listen("tcp", "localhost:50051")
 if err != nil {
  log.Fatalf("failed to listen: %v \n", err)
 }

 grpcServer := grpc.NewServer()
 calculatorPB.RegisterCalculatorServiceServer(grpcServer, &Server{})

 if err := grpcServer.Serve(lis); err != nil {
  log.Fatalf("failed to serve: %v \n", err)
 }
}

STEP 2:實作 Protocol Buffer 中的 service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func (*Server) Sum(ctx context.Context, req *calculatorPB.CalculatorRequest) (*calculatorPB.CalculatorResponse, error) {
 fmt.Printf("Sum function is invoked with %v \n", req)

 a := req.GetA()
 b := req.GetB()

 res := &calculatorPB.CalculatorResponse{
  Result: a + b,
 }

 return res, nil
}

STEP 3:啟動 server

在終端機中輸入:

1
go run server/server.go

即可啟動 gRPC server。

補充:使用 Bloom RPC 進行測試

在只有 server 的情況下,可以使用 BloomRPC 這套工具來模擬 Client 對 gRPC server 發送請求,功能就類似在 Restful 中使用的 Postman。

使用時只需要匯入 proto 檔後,即可看到對應可呼叫的方法和可帶入的參數,能這麼方便也是因為在 protocol buffer 中已經把傳輸的資料格式和能對應呼叫的方法都定好的緣故。


實作 gRPC Client

完整程式碼可檢視 besg-grpc repository 中的 client 資料夾。

STEP 1:與 gRPC server 建立連線

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func main() {
 conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
 if err != nil {
  log.Fatalf("failed to dial: %v", err)
 }

 defer conn.Close()

 client := calculatorPB.NewCalculatorServiceClient(conn)

 doUnary(client)
}

STEP 2:使用 Protocol Buffers 中定義好的 Service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func doUnary(client calculatorPB.CalculatorServiceClient) {
 fmt.Println("Staring to do a Unary RPC")
 req := &calculatorPB.CalculatorRequest{
  A: 3,
  B: 10,
 }

 res, err := client.Sum(context.Background(), req)
 if err != nil {
  log.Fatalf("error while calling CalculatorService: %v \n", err)
 }

 log.Printf("Response from CalculatorService: %v", res.Result)
}

STEP 3:向 server 發送請求

在終端機中輸入:

1
go run client/client.go

即可執行 client.go 並向剛剛起動好的 server 發送請求。


gRPC 解決了什麼

gRPC 和 REST API 的比較

比較 gRPC 服務與 HTTP API @ microsoft docs

簡單來說,gRPC 在效能上比起 REST API 好非常多:

項目gRPCRestful API
資料傳輸格式(Payload)Protocol Buffer - 更快且更小JSON, XML, formData - 較慢且較大
通訊協定HTTP/2HTTP
傳輸方式支援一般的「請求-回應」、伺服器端串流、Client 端串流、與雙向串流(streaming)僅能透過 Client 發送請求、Server 給予回應
API 方法命名沒有限制,一般會直接描述該方法要做的事,例如 createUser, getUser。不需要思考路由命名。使用動詞(GET, POST, PUT, PATCH, DELETE)搭配資源來命名。需要根據不同的行為來定義不同的路由。
Client 呼叫 API 的方式就像呼叫一般的函式透過特定的 Endpoint,給予符合的資料型別
Server 建立 API 的方式根據文件(Protocol Buffer)實作功能,不需要額外檢查資料型別與方法正確性。根據文件(Swagger)實作功能,但須額外檢查資料型別。
根據文件產生程式碼Protocol BuffersOpenAPI / Swagger

此外,gRPC 的 server,預設就是非同步的,因此不會阻塞任何進來的請求,並可以平行處理多個請求。gRPC Client 則可以選擇要用同步(阻塞)或非同步的方式處理。


使用 Protocol Buffers 的好處

  • 節省網路傳輸量:速度更快、檔案更小
  • 節省 CPU 消耗:Parse JSON 本身是 CPU intensive 的任務;Parse Protocol Buffer(binary format)因為更接近底層機器表徵資料的方式,消耗的 CPU 資源較低
  • 跨程式語言:Protocol Buffer 可以根據不同的程式語言編譯出不同的檔案
  • 可以寫註解、型別清楚明確

TIP
節省網路傳輸量和 CPU 消耗在行動裝置上的影響可能更重要。


跨程式語言的好處

透過 Protocol Buffer 定義好資料的傳輸欄位(message)和呼叫的方法(service)後,gRPC 即可在不同程式語言上運行,這非常適合微服務(micro-services)的應用情境,只要雙方一起定義好 schema 後,就可以用不同的程式語言進行開發。


使用 HTTP/2 的好處

傳統的 HTTP/1.1 在每個 TCP 連線中只允許向 server 發送單一個請求,但當網頁載入時,往往會需要向同一個伺服器發送多個請求(例如、圖檔、CSS、靜態檔、JS 等),因此為了要避開這樣的限制、加快載入的速度,瀏覽器會實作多個平行的(parallel) TPC 連線(每個瀏覽器實作不同,因此數量的上限也不同),以處理同時向伺服器發出的多個請求。

在 HTTP/2 中則可在同一個 TCP 連線中進行多個請求和回應,並且可以由 server 主動推送資源給 client,而並非一定要透過 client 主動請求;此外支援 HTTP Header 的壓縮,減少資料傳數量;HTTP/2 也是使用 binary 的方式在傳輸資料。


gRPC 的四種類型

  • Unary:類似傳統 API,client 發送 request 而 server 回傳 response
  • Server Streaming:透過 HTTP/2,client 發送一次 request,而 server 可以回傳多次資料
  • Client Streaming:client 發送多次資料,直到告知 server 資料傳完後,server 再給予 response
  • Bi Directional Streaming:兩邊都用串流的方式傳送資料

圖片來源:gRPC Golang - Master Class: Build Modern API & MicroServices @ Udemy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
service GreetService {
  // Unary
  rpc Greet(GreetRequest) returns (GreetResponse) {};

  // Streaming Server
  rpc GreetManyTimes(GreetManyTimesRequest) returns (stream GreetManyTimesResponse) {};

  // Streaming Client
  rpc LongGreet(stream LongGreetRequest) returns (LongGreetResponse) {};

  // Bi-directional Streaming
  rpc GreetEveryone(stream GreetEveryoneRequest) returns (stream GreetEveryoneResponse) {};
}

gRPC 的缺點

  • Protocol Buffer 不像 JSON 是 Human Readable。
  • 需要額外的學習時間和導入成本。
  • 瀏覽器原生目前還不支援,須透過套件 grpc-web 來處理。

其他

推薦工具

  • BloomRPC:方便用來模擬 Client 對 gRPC server 發送請求,功能就類似在 Restful 中使用的 Postman。

錯誤排除

protoc-gen-go: program not found or is not executable

1
2
# 需要把 $GOPATH/bin 加到 .zshrc/.bashrc 等
$ echo 'export PATH=$PATH:$GOPATH/bin' >> $HOME/.zshrc

參考資料


Licensed under CC BY-NC-SA 4.0
comments powered by Disqus