以前學習的go的微服務之間仍是經過REST API
的方式互相調用的,但既然要學習微服務,gRPC
確定是一個繞不過去的須要學習的技術, 因此就開搞吧java
gRPC
是一款語言中立、平臺中立、開源的遠程過程調用系統git
即:
gRPC
客戶端和服務端能夠在多種環境中運行和交互,例如用java
寫一個服務端,能夠用go語言寫客戶端調用github
微服務架構中,因爲每一個服務對應的代碼庫是獨立運行的,沒法直接調用,彼此間的通訊就是個大問題.golang
gRPC能夠實現將大的項目拆分爲多個小且獨立的業務模塊,也就是服務。各服務間使用高效的protobuf
協議進行RPC調用,gRPC默認使用protocol buffers
,這是google開源的一套成熟的結構數據序列化機制shell
固然也能夠使用其餘數據格式如JSONvim
能夠用proto files建立gRPC服務,用message類型來定義方法參數和返回類型bash
以下圖,解壓出來因平臺而異會是一個protoc
或者protoc.exe
架構
# 打開存放環境變量的文件 vim ~/.bash_profile # 添加以下,後面是路徑 alias protoc="/Users/emm/others/protoc-3.12.4-osx-x86_64/bin/protoc" # 刷新環境變量 source ./.bash_profile
go get github.com/golang/protobuf/protoc-gen-go函數
安裝後會在GOPATH
目錄下生成可執行文件,protobuf的編譯器插件protoc-gen-go
,等下執行protoc
命令會自動調用這個插件
這裏新建一個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表明順序 }
而後運行如下的命令來生成.go
結尾的文件
protoc
包以及protoc-gen-go
插件的做用# 編譯Product.proto以後輸出到service文件夾 protoc --go_out=../service Product.proto
以下就在service文件夾自動生成了一個go文件,而且它提示咱們不要去修改它
這個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); }
注意
protoc --go_out=plugins=grpc:../service Product.proto
而後仍是會在service文件夾下生成一個.go
的文件
有兩個比較須要注意的
後面須要在server中調用這個來註冊
咱們須要繼承這個接口,即實現它全部的方法
上面咱們在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 }
前面的都是準備工做,這裏是真正把服務端跑起來的操做
下面是服務端代碼:
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.0grpc_client
文件夾存放客戶端相關的grpc_client
文件夾下再新建一個service
文件夾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) }
而後客戶端輸出正確的結果,第一個go的gRPC調用運行成功