Prerequisites(先決條件)
- Go
Go的三個(gè)最新主要版本之一 - Protocol buffer 編譯器成箫,protoc,version3
http://www.reibang.com/p/5324b412f1de - protocol 編譯器的 Go 插件:
http://www.reibang.com/p/5324b412f1de
獲取示例代碼
- 下載實(shí)例代碼
$ git clone -b v1.46.0 --depth 1 https://github.com/grpc/grpc-go
- 切換代碼目錄
$ cd grpc-go/examples/helloworld
運(yùn)行示例
在 examples/helloworld 目錄下:
- 編譯和執(zhí)行服務(wù)端代碼
$ go run greeter_server/main.go
- 在另外一個(gè)終端中桃笙,編譯和執(zhí)行客戶端代碼,會(huì)看到客戶端輸出:
$ go run greeter_client/main.go Greeting: Hello world
更新 gRPC 服務(wù)
在本節(jié)中蒙幻,將使用額外的 server 方法更新應(yīng)用程序漓雅。gRPC服務(wù)是使用 protocol buffers 定義的。
現(xiàn)在耕渴,只需要知道服務(wù)器和客戶端存根都有一個(gè) SayHello()RPC方法拘悦,該方法從客戶端獲取一個(gè) HelloRequest 參數(shù),并從服務(wù)器返回一個(gè) HelloReply橱脸,該方法的定義如下:
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
打開 helloworld/helloworld.proto础米,添加一個(gè)新的方法 SayHelloAgain()
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
重新生成 gRPC 代碼
在可以使用新的服務(wù)方法之前,需要重新編譯更新后的 .proto 文件添诉。
在 examples/helloworld 目錄下屁桑,運(yùn)行下面的命令:
$ protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
helloworld/helloworld.proto
paths:用于指定生成文件的路徑的結(jié)構(gòu),有兩個(gè)值:
1. paths=import 相對(duì)于導(dǎo)入路徑栏赴, 默認(rèn)值
2. paths=source_relative 相對(duì)于輸入文件蘑斧,即proto文件的路徑
重新生成的 helloworld/helloworld.pb.go 和 helloworld/helloworld_grpc.pb.go 文件中包含:
- xxx.pb.go
它包含用于填充、序列化和檢索 請(qǐng)求和響應(yīng)消息類型 的所有protocol buffer 代碼须眷。 - xxx_grpc.pb.go
客戶端使用的 服務(wù)中定義的方法調(diào)用的接口類型(或存根)乌叶。
服務(wù)器要實(shí)現(xiàn)的接口類型。
更新并運(yùn)行應(yīng)用程序
雖然已經(jīng)生成了客戶端柒爸、服務(wù)端代碼准浴,但是任然需要示例程序中 實(shí)現(xiàn)和調(diào)用 新的方法。
- 更新 服務(wù)端
打開 greeter_server/main.go 添加下面的函數(shù):func (s *server) SayHelloAgain (ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { retrn &pb.HelloReply{Message: "Hello again " + in.GetName()}, nil }
- 更新客戶端
打開 greeter_client/main.go 在 main() 函數(shù)體的最下方添加下面代碼:r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: name}) if err != nil { log.Fatalf("could not greet : %v", err) } log.Printf("Greetine : %s", r.GetMessage())
- 運(yùn)行
# 運(yùn)行 server go run greeter_server/main.go # 運(yùn)行 client go run greeter_client/main.go Alice # client 輸出 2021/05/15 23:55:08 Greeting: Hello Alice 2021/05/15 23:55:08 Greeting: Hello again Alice
總結(jié)
- gRPC基本使用流程
在 .proto 文件中定義 service捎稚、message
通過 protocol buffer 編譯器生成代碼
-
創(chuàng)建服務(wù)端
3.1 實(shí)現(xiàn) servic 中定義的接口
gRPC 允許你定義四種類型的服務(wù)方法3.2 啟動(dòng)一個(gè)gRPC 服務(wù)乐横,監(jiān)聽客戶端請(qǐng)求
一旦你實(shí)現(xiàn)了所有的方法,我們需要啟動(dòng)一個(gè) gRPC 服務(wù)今野,目的是客戶端能夠使用我們的服務(wù)葡公。
如何構(gòu)建并啟動(dòng)一個(gè)服務(wù):
3.2.1 指定用于 監(jiān)聽客戶端請(qǐng)求的端口
lis, err := net.Listen(...)3.2.2 創(chuàng)建一個(gè) gRPC 服務(wù)實(shí)例
grpcServer := grpc.NewServer(...)3.2.3 在 gRPC服務(wù)器上注冊(cè)我們的服務(wù)實(shí)現(xiàn)
pb.RegisterRouterGuideServer(...)3.2.4 在服務(wù)器上使用 端口詳細(xì)信息 調(diào)用Serve() 方法,以執(zhí)行阻塞等待条霜,直到進(jìn)程被終止 或 調(diào)用 Stop()
grpcServer.Serve(...)lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port)) if err != nil { log.Fatalf("failed to listen: %v", err) } var opts []grpc.ServerOption ... grpcServer := grpc.NewServer(opts...) pb.RegisterRouteGuideServer(grpcServer, newServer()) grpcServer.Serve(lis)
創(chuàng)建客戶端
-
創(chuàng)建一個(gè)stub
要調(diào)用服務(wù)方法催什,我們首先需要?jiǎng)?chuàng)建一個(gè)gRPC通道來與服務(wù)器通信。我們通過將服務(wù)器地址和端口號(hào)傳遞給 grpc.Dial() 來創(chuàng)建它宰睡,如下所示:var opts []grpc.DialOption ... conn, err := grpc.Dial(*serverAddr, opts...) if err != nil { ... } defer conn.Close()
可以使用 DialOptions 在 grpc.Dial 中設(shè)置身份驗(yàn)證憑據(jù)(例如蒲凶,TLS气筋、GCE憑據(jù)或JWT憑據(jù)),當(dāng)服務(wù)需要這些憑據(jù)時(shí)可以使用它們旋圆。
一旦 gRPC通道 設(shè)置好宠默,我們就需要一個(gè)客戶端存根來執(zhí)行RPC。我們使用從 .proto文件 生成的 pb包 提供的 NewRouteGuideClient方法 獲得它灵巧。
client := pb.NewRouteGuideClient(conn)
調(diào)用服務(wù)方法
gRPC允許定義四種類型的服務(wù)方法執(zhí)行客戶端搀矫、服務(wù)端應(yīng)用程序