golang grpc配置使用實戰(zhàn)
什么是PRC&GRPC
RPC是遠程過程調(diào)用(Remote Procedure Call)的縮寫形式, RPC 的主要功能目標是讓構(gòu)建分布式計算(應用)更容易,在提供強大的遠程調(diào)用能力時不損失本地調(diào)用的語義簡潔性。通俗地講,使用RPC進行通信,調(diào)用遠程函數(shù)就像調(diào)用本地函數(shù)一樣,RPC底層會做好數(shù)據(jù)的序列化與傳輸。 下圖是dubbo rpc實現(xiàn)的圖解,以便于大家理解RPC:

GRPC是rpc框架的實現(xiàn),是一個基于Protobuf序列化協(xié)議開發(fā)的高性能,開源和通用的RPC框架,且支持眾多開發(fā)語言。

從圖中還可以看出,proto文件的編譯支持多種語言(Go、Java、Python等),可以輕松實現(xiàn)跨語言調(diào)用。
RPC調(diào)用之前需要進行IDL文件定義編寫和對應語言調(diào)用模板方法生成(protoc自動生成)
RPC調(diào)用大致步驟:
- 客戶端建立連接(gRPC Stub)并調(diào)用A方法,發(fā)起RPC調(diào)用
- gRPC框架對請求信息使用Protobuf進行對象序列化壓縮(IDL)
- 服務端(gPRC Server)接收到請求后,解碼反序列化,進行業(yè)務邏輯處理并返回。
- 對響應結(jié)果使用Protobuf進行對象序列化壓縮(IDL)
- 客戶端接受到服務端響應,解碼發(fā)序列化?;卣{(diào)被調(diào)用的A方法,喚醒正在等待響應(阻塞)的客戶端調(diào)用并返回響應結(jié)果。
Go gRPC 環(huán)境準備
本人是在WSL環(huán)境(window linux 子系統(tǒng))進行的,window 和 mac 可以自行嘗試,原理和步驟都一樣。
Go 語言環(huán)境安裝,下載對應的安裝包,配置GOPATH、GOROOT、GOPROXY,以及GO111MODULE 設置為on,具體安裝和配置細節(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編譯器用來編譯 .proto RPC協(xié)議定義文件,自動生成對應語言的目標代碼,減少開發(fā)量。安裝步驟參考:protoc 安裝文檔
lizheng@lz-x:~$ apt install -y protobuf-compiler lizheng@lz-x:~$ protoc --version libprotoc 3.6.1
Protocol buffer Go語言編譯插件配置安裝因為我們使用的是go語言實現(xiàn)grpc,所以 protoc 命令在執(zhí)行編譯的時候,會調(diào)用go語言插件,來生成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
注意:這里安裝完成之后,會將對應命令安裝到gopath配置目錄下的bin文件夾下如下圖

我們需要確保bin下文件命令可以被全局訪問到,配置到PATH中即可,如果是window需要配置到環(huán)境變量path中

實戰(zhàn)編寫和調(diào)用
經(jīng)過上述步驟環(huán)境已經(jīng)完成配置,我們開始一個helloword的程序開發(fā),包括服務端和客戶端兩部分。
- 新建一個文件夾
my-grpc,使用go mod init example.com/ggrpc初始化項目 - 建立子文件夾:client、server、proto,分別存儲客戶端、服務端、grpc存根文件。
- 進入proto,新建一個
helloworld.proto文件,編寫一下內(nèi)容:
// 使用的語法協(xié)議版本 proto3
syntax = "proto3";
package proto;
// 定義生成go文件的package位置和名稱
option go_package = "./;proto";
// 定義Greeter服務
service Greeter {
// 定義SayHello方法,接受HelloRequest消息, 并返回HelloReply消息
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 定義請求對象
message HelloRequest {
string name = 1;
}
// 定義返回對象
message HelloReply {
string message = 1;
}上面只是一個簡單的定義,細節(jié)proto語法可自行官網(wǎng)學習
執(zhí)行protoc命令,生成目標語言文件
# \ 為命令換行但不執(zhí)行,可以寫一行那就不需要 \ 了
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
helloworld.proto執(zhí)行成功會生成:helloworld.pb.go 和 helloworld_grpc.pb.go 兩個文件

5. 執(zhí)行 go mod tidy 下載依賴包,主要是grpc相關的包6. 編寫服務端和客戶端代碼
服務端
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")
)
// 定義一個server實現(xiàn)UnimplementedGreeterServer
// UnimplementedGreeterServer 是第四步自動生成的,可以打開對應文件查看
type server struct {
pb.UnimplementedGreeterServer
}
// server 重寫SayHello方法,做業(yè)務處理
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("回復客戶端的消息: %s", ms)
return &pb.HelloReply{Message: ms}, nil
}
func main() {
// 解析命令行參數(shù)
flag.Parse()
// 監(jiān)聽本地tcp端口
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// 創(chuàng)建一個grpc Server服務對象,Handler非必傳
// s := grpc.NewServer() // 可以直接創(chuàng)建對象
s := grpc.NewServer(grpc.StatsHandler(&h.MyHandler{}))
// 注冊服務
pb.RegisterGreeterServer(s, &server{})
// 啟動RPC并監(jiān)聽
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()
// 連接服務端
conn, err := grpc.Dial(*serAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("連接服務器失敗: %v", err)
}
log.Printf("建立連接成功: %s", *serAddr)
// 執(zhí)行完方法自動關閉資源
defer conn.Close()
// 創(chuàng)建客戶端
c := pb.NewGreeterClient(conn)
log.Println("5秒中之后調(diào)用SayHello方法")
time.Sleep(time.Second * 5)
// 創(chuàng)建2秒超時ctx
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
// 發(fā)起RPC請求
log.Println("開始調(diào)用SayHello方法")
res, err := c.SayHello(ctx, &pb.HelloRequest{Name: "一號"})
if err != nil {
log.Fatalf("請求失敗: %v", err)
}
log.Printf("請求結(jié)果: %s", res.GetMessage())
// 睡眠一會再結(jié)束
log.Println("3秒后結(jié)束,客戶端自動斷開連接")
time.Sleep(time.Second * 3)
}因為本人測試加了一個server端的handler,給出handle的代碼,可以忽略
package handler
import (
"context"
"log"
"google.golang.org/grpc/stats"
)
// 自定義handler實現(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)
}到此這篇關于golang grpc配置使用實戰(zhàn)的文章就介紹到這了,更多相關golang grpc配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go語言 channel如何實現(xiàn)歸并排序中的merge函數(shù)詳解
這篇文章主要給大家介紹了關于Go語言 channel如何實現(xiàn)歸并排序中merge函數(shù)的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2018-02-02
Go語言動態(tài)并發(fā)控制sync.WaitGroup的靈活運用示例詳解
本文將講解 sync.WaitGroup 的使用方法、原理以及在實際項目中的應用場景,用清晰的代碼示例和詳細的注釋,助力讀者掌握并發(fā)編程中等待組的使用技巧2023-11-11

