甚麼是gRPC
gRPC 是Google所開源的一個RPC(Remote Procedure Calls)系統,RPC就如名字所說是一個讓我們能對遠端程式/程序的呼叫如同在本地端電腦呼叫程式一樣的系統,這樣的系統解決了分布式系統中Server間程式的調用問題。而server間互相溝通的方式還有如WebSocket, RestfulAPI等,而相較於這些方法,gRPC提供了更輕量更快速的呼叫返回。gRPC主要使用了Protocal Buffers作為介面描述語言(IDL),適合用在高性能,需要快速響應的場景。gPRC提供了四種形式的傳輸,包括Client傳一個請求,Server回一個響應、Client傳多個請求(streaming),Server回一個響應、Client傳一個請求,Server streaming多個響應、及Client Server均已streaming方式交互 這四種形式,本文以最基本的一對一的為例子來實現一個簡單的加法器(Client傳送兩個數字到Server的Add函數,然後返回相加的結果給Client)。
安裝protoc
不同於WS 或API,gRPC需要安裝protobuf才能進行繼續的開發,這是因為我們首先會編寫.proto (protocal buffers檔案)來定義需要傳輸的資料(message)及使用的程式(service),需要將我們編寫的.proto轉換成不同程式語言的Stub,Stub會提供介面及函數來供開發的程式呼叫,而安裝的protobuf CLI能幫我們進行此轉換。
此連結(https://github.com/protocolbuffers/protobuf/releases)包含了protoc的source code及不同系統的編譯完成的版本,因為我們在windows上開發,下載win64的版本後解壓縮即可使用,如下圖:
記得要去設定環境變數,於系統環境變數中的Path新增此protoc bin檔的路徑(如筆者為例, 新增 C:\Program Files\protoc-21.5-win64\bin),以讓console輸入時能找到,完成後,打開console輸入 protoc –version,返回libprotoc x.x.x 表示成功囉。
接著,為了讓protoc能編譯出go 的程式(因為我們以go來開發),需要安裝go 的套件如下:
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
寫我們程式要用的.Proto
我們的應用很簡單:client傳送兩個數字,並呼叫server提供的Add函數,server收到後執行Add函數並傳回結果給client。因此我們定義一個Request,包含兩個Int32的變數,一個Result,包含一個Int32的變數,並定義一個名為Add的Service,.proto檔案詳如下:
syntax = "proto3"; // 定義要使用的 protocol buffer 版本
package adder;
option go_package = ".;adder"; // for go 定義路徑
service AddService {
rpc Add(AdderRequest) returns (AdderResult) {}
}
message AdderRequest {
int32 a = 1;
int32 b = 2;
}
message AdderResult {
int32 result = 1;
}
其中syntax =”proto3″ 讓protoc知道編譯的版本。完成後我們存為adder.proto檔。
接著,我們於adder.proto檔案的同一個資料夾執行$ protoc –go_out=. –go_opt=paths=source_relative –go-grpc_out=. –go-grpc_opt=paths=source_relative adder.proto,會發現產生了adder.pb.go及adder_grpc.pb.go兩個go的檔案,有興趣可以看看這兩個檔案的內容,基本上就是將我們上述的定義轉換成了gRPC的go程式碼。這兩個程式基本上我們不會去改動它,但會呼叫它的功能及物件定義。
開發Server
在Server中我們要實作Add這個func,並建立一個listen tcp的port來等待client的呼叫,直接上code比較清楚:
package main
import (
"context"
"fmt"
"net"
"os"
adder "grpc/adder" //此為 adder.pb.go 包的路徑
"google.golang.org/grpc"
)
type Server struct {
adder.UnimplementedAddServiceServer
}
//實作Add這個被呼叫的函數
func (*Server) Add(ctx context.Context, req *adder.AdderRequest) (*adder.AdderResult, error) {
fmt.Printf("input data: %v \n", req)
a := req.GetA()
b := req.GetB()
result := &adder.AdderResult{
Result: a + b,
}
return result, nil
}
func main() {
fmt.Println("starting gRPC server")
listen, err := net.Listen("tcp", "localhost:50081")
if err != nil {
fmt.Printf("Listen 失敗, error= %v", err)
}
grpcServer := grpc.NewServer()
adder.RegisterAddServiceServer(grpcServer, &Server{})
if err := grpcServer.Serve(listen); err != nil {
fmt.Printf("serve 失敗, err=%v", err)
os.Exit(1)
}
}
開發Client
Client重點就是建立與Server的連線,然後呼叫Add的函數了,看code如何寫來達成我們的目的囉。
package main
import (
"context"
"fmt"
adder "grpc/adder"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
fmt.Println("starting grpc client")
conn, err := grpc.Dial("localhost:50081", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Printf("Dial 失敗, error=%v", err)
}
defer conn.Close()
client := adder.NewAddServiceClient(conn)
req := &adder.AdderRequest{
A: 9,
B: 109,
}
result, err := client.Add(context.Background(), req)//如同呼叫本地函數一樣
if err != nil {
fmt.Printf("Add 失敗, error=%v", err)
}
fmt.Printf("result is : %v \n", result)
fmt.Println("end client")
}
以上就是一個很簡單的Go的 gRPC入門的程式,若有任何問題歡迎留言討論囉。