1. 前言
之前學習的go的微服務之間還是通過REST API
的方式互相呼叫的,但既然要學習微服務,gRPC
肯定是一個繞不過去的需要學習的技術, 所以就開搞吧
2. gRPC與Protobuf簡介
gRPC
是一款語言中立、平臺中立、開源的遠端過程呼叫系統
即:
gRPC
客戶端和服務端可以在多種環境中執行和互動,例如用java
寫一個服務端,可以用go語言寫客戶端呼叫
微服務架構中,由於每個服務對應的程式碼庫是獨立執行的,無法直接呼叫,彼此間的通訊就是個大問題.
gRPC可以實現將大的專案拆分為多個小且獨立的業務模組,也就是服務。各服務間使用高效的protobuf
協議進行RPC呼叫,gRPC預設使用protocol buffers
,這是google開源的一套成熟的結構資料序列化機制
當然也可以使用其他資料格式如JSON
可以用proto files建立gRPC服務,用message型別來定義方法引數和返回型別
3. 安裝
- 第一步:下載grpc通用編譯器
如下圖,解壓出來因平臺而異會是一個protoc
或者protoc.exe
- 第二步:把下載的二進位制檔案路徑新增到環境變數中(為了能全域性訪問protoc)
- 這裡以為mac為例子
# 開啟存放環境變數的檔案
vim ~/.bash_profile
# 新增如下,後面是路徑
alias protoc="/Users/emm/others/protoc-3.12.4-osx-x86_64/bin/protoc"
# 重新整理環境變數
source ./.bash_profile
- 第三步: 安裝go專用的protoc的生成器
go get github.com/golang/protobuf/protoc-gen-go
安裝後會在GOPATH
目錄下生成可執行檔案,protobuf的編譯器外掛protoc-gen-go
,等下執行protoc
命令會自動呼叫這個外掛
4. 中間檔案演示
4.1 編寫中間檔案
這裡新建一個pbfiles資料夾用於存放protoc
檔案
// 這個就是protobuf的中間檔案
// 指定的當前proto語法的版本,有2和3
syntax = "proto3";
// 指定等會檔案生成出來的package
package service;
// 定義request
message ProductRequest{
int32 prod_id = 1; // 1代表順序
}
// 定義response
message ProductResponse{
int32 prod_stock = 1; // 1代表順序
}
4.2 執行protoc命令編譯成go中間檔案
然後執行以下的命令來生成.go
結尾的檔案
- 下面的命令就是我們剛剛下的
protoc
包以及protoc-gen-go
外掛的作用
# 編譯Product.proto之後輸出到service資料夾
protoc --go_out=../service Product.proto
如下就在service資料夾自動生成了一個go檔案,並且它提示我們不要去修改它
5. 建立gRPC服務端
5.1 新建Product.protoc
這個protoc檔案比上面的多出了一個service的定義和裡面的一個方法的定義
// 這個就是protobuf的中間檔案
// 指定的當前proto語法的版本,有2和3
syntax = "proto3";
// 指定等會檔案生成出來的package
package service;
// 定義request model
message ProductRequest{
int32 prod_id = 1; // 1代表順序
}
// 定義response model
message ProductResponse{
int32 prod_stock = 1; // 1代表順序
}
// 定義服務主體
service ProdService{
// 定義方法
rpc GetProductStock(ProductRequest) returns(ProductResponse);
}
5.2 執行protoc命令
注意
- 這裡的protoc命令和之前的命令相比有點不一樣
protoc --go_out=plugins=grpc:../service Product.proto
然後還是會在service資料夾下生成一個.go
的檔案
有兩個比較需要注意的
- RegisterProdServiceServer
後面需要在server中呼叫這個來註冊
- ProdServiceServer的介面定義
我們需要繼承這個介面,即實現它所有的方法
5.3 實現RegisterProdServiceServer介面
上面我們在protoc
檔案中定義了一個ProdService
中包含了一個GetProductStock
的方法
這裡我們要實現自動生成的go檔案中的介面
package service
import "context"
type ProdService struct {
}
func (ps *ProdService) GetProductStock(ctx context.Context, request *ProductRequest) (*ProductResponse, error) {
return &ProductResponse{ProdStock: request.ProdId}, nil
}
5.4 準備工作完成,建立main函式將服務端跑起來
前面的都是準備工作,這裡是真正把服務端跑起來的操作
下面是服務端程式碼:
package main
import (
"gomicro-quickstart/grpc_demo/service"
"google.golang.org/grpc"
"log"
"net"
)
func main() {
// 1. new一個grpc的server
rpcServer := grpc.NewServer()
// 2. 將剛剛我們新建的ProdService註冊進去
service.RegisterProdServiceServer(rpcServer, new(service.ProdService))
// 3. 新建一個listener,以tcp方式監聽8082埠
listener, err := net.Listen("tcp", ":8082")
if err != nil {
log.Fatal("服務監聽埠失敗", err)
}
// 4. 執行rpcServer,傳入listener
_ = rpcServer.Serve(listener)
}
排坑:
- 如果遇見類似
undefined: grpc.SupportPackageIsVersion6
和undefined: grpc.ClientConnInterface
的錯誤,可以修改go.mod將grpc版本改到1.27.0
6. 建立gRPC客戶端
- 新建一個
grpc_client
資料夾存放客戶端相關的 - 並在
grpc_client
資料夾下再新建一個service
資料夾
6.1 拷貝Product.pb.go到客戶端service資料夾下
6.2 編寫client的main函式
package main
import (
"context"
"fmt"
"gomicro-quickstart/grpc_client/service"
"google.golang.org/grpc"
"log"
)
func main() {
// 1. 新建連線,埠是服務端開放的8082埠
// 並且新增grpc.WithInsecure(),不然沒有證照會報錯
conn, err := grpc.Dial(":8082", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
// 退出時關閉連結
defer conn.Close()
// 2. 呼叫Product.pb.go中的NewProdServiceClient方法
productServiceClient := service.NewProdServiceClient(conn)
// 3. 直接像呼叫本地方法一樣呼叫GetProductStock方法
resp, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233})
if err != nil {
log.Fatal("呼叫gRPC方法錯誤: ", err)
}
fmt.Println("呼叫gRPC方法成功,ProdStock = ", resp.ProdStock)
}
6.3 執行並顯示結果
- 先把服務端執行起來
- 再把客戶端執行起來
然後客戶端輸出正確的結果,第一個go的gRPC呼叫執行成功