[筆記] 實作分散式計分系統(三) : gRPC and Protobuf
在建立 gRPC Server 前,須先了解 Protobuf, 而其兩者都是 Google 的產物。
這邊並不會詳細介紹它們的詳細概念, 這類的筆記網上已有很多, 所以只做個簡介並紀錄如何使用它們搭建 gRPC Server。
Protobuf
Protobuf 是 Protocol Buffers的簡稱,用於描述一種輕便高效的結構化數據存儲格式,簡單說就是能以一份文件定義格式,然後能方便直接轉成各類語言的資料型態,而以本範例來說能直接轉譯成 golang struct,目前已能支援多種語言,詳細可參考官網。
gRPC
gRPC 的概念其實很直覺,就是透過 Protobuf 定義出來的資料結構,不只代表格式,同時也代表你伺服器的接口。最大好處其背後是以 http2 傳遞,比起一般走 http/https 的 Restful API 不僅傳輸效率上快上許多,也不用另外寫文件。Client 端與 Server 端只要遵照定義出來的資料格式傳遞/實作(可參考上圖),API 就能執行。
How to use Protobuf
Step1 :
安裝所需套件。
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
Step2:
創建資料夾並定義 .proto
文件。
pb // 擺放 Protobuf 文件
└── game.proto
Step3:
message 用來定義 RPC function 之 Request/Response
這邊共定義了 4 個 function
• GetScore : 獲取指定棒球比賽得分數
• PutScore : 對指定比賽新增分數
• GetGameList: 獲取所有棒球比賽清單
• PostNewGame: 新增棒球比賽
並掛到 rpc Server - ServiceServer 上
Step4 :
進入 /pb
資料夾,然後執行下列指令
protoc --go_out=plugins=grpc:. *.proto
可看到 ./pb
生成 game.pb.go
pb
├── game.pb.go
└── game.proto
How to use gRPC
Step1:
安裝所需套件
go get google.golang.org/grpc
Step2:
建立對應資料夾與 go file
├── pb
│ ├── game.pb.go
│ └── game.proto
├── conf
│ └── app.ini
├── setting
│ └── setting.go
├── server
│ └── serverNode.go
├── client
│ ├── client_demo.go
Step3:
編輯 conf/app.ini
與 setting/setting.go
(這把 configuration 放到檔案裡比較好維護不是必須步驟,只是個人習慣把 configuration 放到檔案裡比較好維護)
這邊是以 “github.com/go-ini/ini”
套件將 conf/app.ini
轉為結構實體變數,能方便在後續使用到它們。
conf/app.ini
setting/setting.go
Step4:
這邊是本篇較重要的地方,首先宣告了 server struct{}
,並依照先前在 game.proto
宣告過方法與 message 將四個同名 function GetScore
PutScore
GetGameList
PostNewGame
實作於 server struct{}
。
以 GetScore
為例,已於 game.proto
定義此 RPC function 所需的參數 - GetScoreRequest
與返回格式 - GetScoreReply
故在 GetScore
function 中接收的參數需為 GetScoreRequest
,最後也以 GetScoreReply
返回。而若 GetScore
執行過程有錯,gRPC 可將 error
帶在第二個參數返回。
再來直接看到完整的 serverNode.go
,連接資料庫的部份定義了 mongoDB()
並帶入 url 與 port 來去連接指定的 mongoDB ,在 GetScore
PutScore
GetGameList
PostNewGame
都有調用到此方法。當然在一般 monolithic system 而言資料庫會保持連線,不會每執行一次服務就重新去連線一次,而這邊會這樣設計是因為本系統有 3 個 Replica Set Node,3 個 node 都能被 Read ,但只有 Primary Node 能執行寫入動作 (Write)。
所以在 GetScore
GetGameList
方法中能讓使用者指定資料庫 node 去做讀取,而 PutScore
PostNewGame
則連接於 conf/app.ini
內的預設 Primary Node)。
serverNode.go
最後則於 main
function 啟動 gRPC Server 並監聽tcp port 50051
。
Step5:
完成 Server 端後再來是 Client 端的部份,這部份就簡單許多 。以新增棒球比賽 PostNewGame
為例,先透過 grpc.Dial()
去連接已啟動的 gRPC Server,在用回傳的 conn
去呼叫 PostNewGame
,當然別忘了要以 game.proto 定義的格式帶入參數 - PostNewGameRequest
。
Step6:
最後來看下目前成果 ,在此前已先啟動 Replca Set 與 gRPC Server,接著再執行 client_demo.go
去新增一筆名為 “Test0109_3”
的棒球比賽。若新增成功回傳的結果會是該筆資料的 objectID
。
當然如果不用其他 RPC Tool 的前提下,那每次 client 端都要透過以上這樣的 client_demo.go
去呼叫服務未免有些麻煩,所以下篇將介紹 Cobra,說明如何將其包裝成 CLI 來方便操作。