golang grpc配置使用實(shí)戰(zhàn)
什么是PRC&GRPC
RPC是遠(yuǎn)程過(guò)程調(diào)用(Remote Procedure Call)的縮寫(xiě)形式, RPC 的主要功能目標(biāo)是讓構(gòu)建分布式計(jì)算(應(yīng)用)更容易,在提供強(qiáng)大的遠(yuǎn)程調(diào)用能力時(shí)不損失本地調(diào)用的語(yǔ)義簡(jiǎn)潔性。通俗地講,使用RPC進(jìn)行通信,調(diào)用遠(yuǎn)程函數(shù)就像調(diào)用本地函數(shù)一樣,RPC底層會(huì)做好數(shù)據(jù)的序列化與傳輸。 下圖是dubbo rpc實(shí)現(xiàn)的圖解,以便于大家理解RPC:
GRPC是rpc框架的實(shí)現(xiàn),是一個(gè)基于Protobuf序列化協(xié)議開(kāi)發(fā)的高性能,開(kāi)源和通用的RPC框架,且支持眾多開(kāi)發(fā)語(yǔ)言。
從圖中還可以看出,proto文件的編譯支持多種語(yǔ)言(Go、Java、Python等),可以輕松實(shí)現(xiàn)跨語(yǔ)言調(diào)用。
RPC調(diào)用之前需要進(jìn)行IDL文件定義編寫(xiě)和對(duì)應(yīng)語(yǔ)言調(diào)用模板方法生成(protoc自動(dòng)生成)
RPC調(diào)用大致步驟:
- 客戶端建立連接(gRPC Stub)并調(diào)用A方法,發(fā)起RPC調(diào)用
- gRPC框架對(duì)請(qǐng)求信息使用Protobuf進(jìn)行對(duì)象序列化壓縮(IDL)
- 服務(wù)端(gPRC Server)接收到請(qǐng)求后,解碼反序列化,進(jìn)行業(yè)務(wù)邏輯處理并返回。
- 對(duì)響應(yīng)結(jié)果使用Protobuf進(jìn)行對(duì)象序列化壓縮(IDL)
- 客戶端接受到服務(wù)端響應(yīng),解碼發(fā)序列化?;卣{(diào)被調(diào)用的A方法,喚醒正在等待響應(yīng)(阻塞)的客戶端調(diào)用并返回響應(yīng)結(jié)果。
Go gRPC 環(huán)境準(zhǔn)備
本人是在WSL環(huán)境(window linux 子系統(tǒng))進(jìn)行的,window 和 mac 可以自行嘗試,原理和步驟都一樣。
Go 語(yǔ)言環(huán)境安裝,下載對(duì)應(yīng)的安裝包,配置GOPATH、GOROOT、GOPROXY,以及GO111MODULE 設(shè)置為on,具體安裝和配置細(xì)節(jié)可參考官網(wǎng)和其他教程,這里列出自己的go env信息:
# GO111MODULE on模式 GO111MODULE="on" GOARCH="amd64" GOBIN="" GOCACHE="/home/lizheng/.cache/go-build" GOENV="/home/lizheng/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/home/lizheng/gopath/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" # GOPATH 配置 GOPATH="/home/lizheng/gopath" GOPRIVATE="" # GOPROXY 配置 GOPROXY="https://goproxy.cn" # GOROOT 配置 GOROOT="/home/lizheng/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/home/lizheng/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.17.7" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/dev/null" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1495300227=/tmp/go-build -gno-record-gcc-switches"
Protocol buffer 編譯器配置這里的Protocol buffer編譯器用來(lái)編譯 .proto RPC協(xié)議定義文件,自動(dòng)生成對(duì)應(yīng)語(yǔ)言的目標(biāo)代碼,減少開(kāi)發(fā)量。安裝步驟參考:protoc 安裝文檔
lizheng@lz-x:~$ apt install -y protobuf-compiler lizheng@lz-x:~$ protoc --version libprotoc 3.6.1
Protocol buffer Go語(yǔ)言編譯插件配置安裝因?yàn)槲覀兪褂玫氖莋o語(yǔ)言實(shí)現(xiàn)grpc,所以 protoc 命令在執(zhí)行編譯的時(shí)候,會(huì)調(diào)用go語(yǔ)言插件,來(lái)生成golang代碼。
lizheng@lz-x:~$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 lizheng@lz-x:~$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
注意:這里安裝完成之后,會(huì)將對(duì)應(yīng)命令安裝到gopath
配置目錄下的bin
文件夾下如下圖
我們需要確保bin下文件命令可以被全局訪問(wèn)到,配置到PATH中即可,如果是window需要配置到環(huán)境變量path中
實(shí)戰(zhàn)編寫(xiě)和調(diào)用
經(jīng)過(guò)上述步驟環(huán)境已經(jīng)完成配置,我們開(kāi)始一個(gè)helloword的程序開(kāi)發(fā),包括服務(wù)端和客戶端兩部分。
- 新建一個(gè)文件夾
my-grpc
,使用go mod init example.com/ggrpc
初始化項(xiàng)目 - 建立子文件夾:client、server、proto,分別存儲(chǔ)客戶端、服務(wù)端、grpc存根文件。
- 進(jìn)入proto,新建一個(gè)
helloworld.proto
文件,編寫(xiě)一下內(nèi)容:
// 使用的語(yǔ)法協(xié)議版本 proto3 syntax = "proto3"; package proto; // 定義生成go文件的package位置和名稱 option go_package = "./;proto"; // 定義Greeter服務(wù) service Greeter { // 定義SayHello方法,接受HelloRequest消息, 并返回HelloReply消息 rpc SayHello (HelloRequest) returns (HelloReply) {} } // 定義請(qǐng)求對(duì)象 message HelloRequest { string name = 1; } // 定義返回對(duì)象 message HelloReply { string message = 1; }
上面只是一個(gè)簡(jiǎn)單的定義,細(xì)節(jié)proto語(yǔ)法可自行官網(wǎng)學(xué)習(xí)
執(zhí)行protoc
命令,生成目標(biāo)語(yǔ)言文件
# \ 為命令換行但不執(zhí)行,可以寫(xiě)一行那就不需要 \ 了 protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ helloworld.proto
執(zhí)行成功會(huì)生成:helloworld.pb.go 和 helloworld_grpc.pb.go 兩個(gè)文件
5. 執(zhí)行 go mod tidy
下載依賴包,主要是grpc相關(guān)的包6. 編寫(xiě)服務(wù)端和客戶端代碼
服務(wù)端
package main import ( "context" "flag" "fmt" "log" "net" "time" h "example.com/ggrpc/handler" pb "example.com/ggrpc/proto" "google.golang.org/grpc" ) var ( port = flag.Int("port", 8000, "The server port") ) // 定義一個(gè)server實(shí)現(xiàn)UnimplementedGreeterServer // UnimplementedGreeterServer 是第四步自動(dòng)生成的,可以打開(kāi)對(duì)應(yīng)文件查看 type server struct { pb.UnimplementedGreeterServer } // server 重寫(xiě)SayHello方法,做業(yè)務(wù)處理 func (s *server) SayHello(c context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) { log.Printf("接收到客戶端的消息: %v", req.GetName()) time.Sleep(time.Second) ms := fmt.Sprintf("好的收到,%s %s", req.GetName(), time.Now()) log.Printf("回復(fù)客戶端的消息: %s", ms) return &pb.HelloReply{Message: ms}, nil } func main() { // 解析命令行參數(shù) flag.Parse() // 監(jiān)聽(tīng)本地tcp端口 lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) if err != nil { log.Fatalf("failed to listen: %v", err) } // 創(chuàng)建一個(gè)grpc Server服務(wù)對(duì)象,Handler非必傳 // s := grpc.NewServer() // 可以直接創(chuàng)建對(duì)象 s := grpc.NewServer(grpc.StatsHandler(&h.MyHandler{})) // 注冊(cè)服務(wù) pb.RegisterGreeterServer(s, &server{}) // 啟動(dòng)RPC并監(jiān)聽(tīng) log.Printf("server listening at %v", lis.Addr()) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }
客戶端
package main import ( "context" "flag" "log" "time" pb "example.com/ggrpc/proto" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) var serAddr = flag.String("addr", "localhost:8000", "the address to connect to") func main() { // 解析命令行參數(shù) flag.Parse() // 連接服務(wù)端 conn, err := grpc.Dial(*serAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { log.Fatalf("連接服務(wù)器失敗: %v", err) } log.Printf("建立連接成功: %s", *serAddr) // 執(zhí)行完方法自動(dòng)關(guān)閉資源 defer conn.Close() // 創(chuàng)建客戶端 c := pb.NewGreeterClient(conn) log.Println("5秒中之后調(diào)用SayHello方法") time.Sleep(time.Second * 5) // 創(chuàng)建2秒超時(shí)ctx ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) defer cancel() // 發(fā)起RPC請(qǐng)求 log.Println("開(kāi)始調(diào)用SayHello方法") res, err := c.SayHello(ctx, &pb.HelloRequest{Name: "一號(hào)"}) if err != nil { log.Fatalf("請(qǐng)求失敗: %v", err) } log.Printf("請(qǐng)求結(jié)果: %s", res.GetMessage()) // 睡眠一會(huì)再結(jié)束 log.Println("3秒后結(jié)束,客戶端自動(dòng)斷開(kāi)連接") time.Sleep(time.Second * 3) }
因?yàn)楸救藴y(cè)試加了一個(gè)server端的handler,給出handle的代碼,可以忽略
package handler import ( "context" "log" "google.golang.org/grpc/stats" ) // 自定義handler實(shí)現(xiàn)stats.Handler打印一些信息 type MyHandler struct { } func (h *MyHandler) TagRPC(c context.Context, tag *stats.RPCTagInfo) context.Context { log.Printf("TagRPC: %v", tag) return c } func (h *MyHandler) HandleRPC(c context.Context, s stats.RPCStats) { log.Printf("HandleRPC: %v", s) } func (h *MyHandler) TagConn(c context.Context, tag *stats.ConnTagInfo) context.Context { log.Printf("TagConn: %v", tag) return c } func (h *MyHandler) HandleConn(c context.Context, s stats.ConnStats) { log.Printf("HandleConn: %v", s) }
到此這篇關(guān)于golang grpc配置使用實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)golang grpc配置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言 channel如何實(shí)現(xiàn)歸并排序中的merge函數(shù)詳解
這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言 channel如何實(shí)現(xiàn)歸并排序中merge函數(shù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-02-02Go動(dòng)態(tài)調(diào)用函數(shù)的實(shí)例教程
本文主要介紹了Go動(dòng)態(tài)調(diào)用函數(shù)的實(shí)例教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01Golang語(yǔ)言如何避免空指針引發(fā)的panic詳解
簡(jiǎn)單地說(shuō)go語(yǔ)言的指針類型和C/C++的指針類型用法是一樣的,除了出去安全性的考慮,go語(yǔ)言增加了一些限制,這篇文章主要給大家介紹了關(guān)于Golang語(yǔ)言如何避免空指針引發(fā)panic的相關(guān)資料,需要的朋友可以參考下2022-01-01Go實(shí)現(xiàn)mongodb增刪改查工具類的代碼示例
這篇文章主要給大家介紹了關(guān)于Go實(shí)現(xiàn)mongodb增刪改查工具類的相關(guān)資料,MongoDB是一個(gè)NoSQL數(shù)據(jù)庫(kù),它提供了靈活的文檔存儲(chǔ)模型以及強(qiáng)大的查詢和操作功能,需要的朋友可以參考下2023-10-10Go語(yǔ)言動(dòng)態(tài)并發(fā)控制sync.WaitGroup的靈活運(yùn)用示例詳解
本文將講解 sync.WaitGroup 的使用方法、原理以及在實(shí)際項(xiàng)目中的應(yīng)用場(chǎng)景,用清晰的代碼示例和詳細(xì)的注釋,助力讀者掌握并發(fā)編程中等待組的使用技巧2023-11-11通過(guò)Golang實(shí)現(xiàn)無(wú)頭瀏覽器截圖
在Web開(kāi)發(fā)中,有時(shí)需要對(duì)網(wǎng)頁(yè)進(jìn)行截圖,以便進(jìn)行頁(yè)面預(yù)覽、測(cè)試等操作,本文為大家整理了Golang實(shí)現(xiàn)無(wú)頭瀏覽器的截圖的方法,感興趣的可以了解一下2023-05-05