歡迎訪問我的GitHub
https://github.com/zq2599/blog_demos
內(nèi)容:所有原創(chuàng)文章分類匯總及配套源碼拐纱,涉及Java、Docker、Kubernetes赢底、DevOPS等躯畴;
gRPC學(xué)習(xí)系列文章鏈接
- 在CentOS7部署和設(shè)置GO
- GO的gRPC開發(fā)環(huán)境準(zhǔn)備
- 初試GO版gRPC開發(fā)
- 實(shí)戰(zhàn)四類服務(wù)方法
- gRPC-Gateway實(shí)戰(zhàn)
- gRPC-Gateway集成swagger
本篇概覽
- 本文《gRPC學(xué)習(xí)》系列的第四篇爵政,前文咱們體驗(yàn)了最簡(jiǎn)單的gRPC開發(fā)及汉,編寫客戶端調(diào)用服務(wù)端稠炬,但這只是最簡(jiǎn)單的一種焕阿,在解決實(shí)際問題時(shí)是遠(yuǎn)遠(yuǎn)不夠的;
- 實(shí)際上首启,gRPC允許你定義以下四類服務(wù)方法(以下描述來自<font color="blue">http://doc.oschina.net/grpc</font>):
- 單項(xiàng) RPC暮屡,即客戶端發(fā)送一個(gè)請(qǐng)求給服務(wù)端,從服務(wù)端獲取一個(gè)應(yīng)答毅桃,就像一次普通的函數(shù)調(diào)用(前一篇文章就是此類)褒纲;
- 服務(wù)端流式 RPC愁溜,即客戶端發(fā)送一個(gè)請(qǐng)求給服務(wù)端,可獲取一個(gè)數(shù)據(jù)流用來讀取一系列消息外厂。客戶端從返回的數(shù)據(jù)流里一直讀取直到?jīng)]有更多消息為止代承;
- 客戶端流式 RPC汁蝶,即客戶端用提供的一個(gè)數(shù)據(jù)流寫入并發(fā)送一系列消息給服務(wù)端。一旦客戶端完成消息寫入论悴,就等待服務(wù)端讀取這些消息并返回應(yīng)答掖棉;
- 雙向流式 RPC,即兩邊都可以分別通過一個(gè)讀寫數(shù)據(jù)流來發(fā)送一系列消息膀估。這兩個(gè)數(shù)據(jù)流操作是相互獨(dú)立的幔亥,所以客戶端和服務(wù)端能按其希望的任意順序讀寫,例如:服務(wù)端可以在寫應(yīng)答前等待所有的客戶端消息察纯,或者它可以先讀一個(gè)消息再寫一個(gè)消息帕棉,或者是讀寫相結(jié)合的其他方式。每個(gè)數(shù)據(jù)流里消息的順序會(huì)被保持饼记。
- 本篇的內(nèi)容香伴,就是編碼實(shí)現(xiàn)上述四類服務(wù)方法,并編寫客戶端代碼調(diào)用具则,整個(gè)開發(fā)流程如下圖所示:
在這里插入圖片描述
源碼下載
- 本篇實(shí)戰(zhàn)中的源碼可在GitHub下載到即纲,地址和鏈接信息如下表所示(https://github.com/zq2599/blog_demos):
名稱 | 鏈接 | 備注 |
---|---|---|
項(xiàng)目主頁(yè) | https://github.com/zq2599/blog_demos | 該項(xiàng)目在GitHub上的主頁(yè) |
git倉(cāng)庫(kù)地址(https) | https://github.com/zq2599/blog_demos.git | 該項(xiàng)目源碼的倉(cāng)庫(kù)地址,https協(xié)議 |
git倉(cāng)庫(kù)地址(ssh) | git@github.com:zq2599/blog_demos.git | 該項(xiàng)目源碼的倉(cāng)庫(kù)地址博肋,ssh協(xié)議 |
- 這個(gè)git項(xiàng)目中有多個(gè)文件夾低斋,本章的應(yīng)用在<font color="blue">go-source</font>文件夾下,如下圖紅框所示:
在這里插入圖片描述
- <font color="blue">go-source</font>里面有多個(gè)子文件夾匪凡,本篇的源碼在<font color="red">grpcstream</font>中膊畴,如下圖紅框:
在這里插入圖片描述
提前說明文件和目錄
- 本次實(shí)戰(zhàn)在<font color="blue">$GOPATH/src</font>目錄下新增文件夾<font color="red">grpcstream</font>,里面總共有以下內(nèi)容:
[golang@centos7 src]$ tree grpcstream/
grpcstream/
├── client
│ └── client.go
├── grpcstream.pb.go
├── grpcstream.proto
└── server
└── server.go
- 準(zhǔn)備工作完成病游,接下來正式開始開發(fā)巴比;
編寫proto文件
- proto文件用來描述遠(yuǎn)程服務(wù)相關(guān)的信息,如方法簽名礁遵、數(shù)據(jù)結(jié)構(gòu)等轻绞,本篇的proto文件名為<font color="blue">grpcstream.proto</font>,位置是<font color="red">$GOPATH/src/grpcstream</font>佣耐,內(nèi)容如下(稍后會(huì)指出幾處要注意的地方):
// 協(xié)議類型
syntax = "proto3";
// 包名
package grpcstream;
// 服務(wù)端請(qǐng)求的數(shù)據(jù)結(jié)構(gòu)
message SingleRequest {
int32 id = 1;
}
// 服務(wù)端響應(yīng)的數(shù)據(jù)結(jié)構(gòu)
message SingleResponse {
int32 id = 1;
string name = 2;
}
// 定義的服務(wù)名
service IGrpcStremService {
// 單項(xiàng)RPC :?jiǎn)蝹€(gè)請(qǐng)求政勃,單個(gè)響應(yīng)
rpc SingleReqSingleResp (SingleRequest) returns (SingleResponse);
// 服務(wù)端流式 :?jiǎn)蝹€(gè)請(qǐng)求,集合響應(yīng)
rpc SingleReqMultiResp (SingleRequest) returns (stream SingleResponse);
// 客戶端流式 :集合請(qǐng)求兼砖,單個(gè)響應(yīng)
rpc MultiReqSingleResp (stream SingleRequest) returns (SingleResponse);
// 雙向流式 :集合請(qǐng)求奸远,集合響應(yīng)
rpc MultiReqMultiResp (stream SingleRequest) returns (stream SingleResponse);
}
- 這個(gè)<font color="blue">grpcstream.proto</font>文件有以下幾處要注意的地方:
- 方法<font color="blue">SingleReqSingleResp</font>非常簡(jiǎn)單既棺,和上一篇文章中的demo一樣,入?yún)⑹且粋€(gè)數(shù)據(jù)結(jié)構(gòu)懒叛,服務(wù)端返回的也是一個(gè)數(shù)據(jù)結(jié)構(gòu)丸冕;
- 方法<font color="blue">SingleReqSingleResp</font>是服務(wù)端流式類型,特征是返回值用<font color="red">stream</font>修飾薛窥;
- 方法<font color="blue">MultiReqSingleResp</font>是客戶端流式類型胖烛,特征是入?yún)⒂?lt;font color="red">stream</font>修飾;
- 方法<font color="blue">MultiReqMultiResp</font>是雙向類型诅迷,特征是入?yún)⒑头祷刂刀加?lt;font color="red">stream</font>修飾佩番;
- 似乎有規(guī)律可循:客戶端如果想和服務(wù)端建立通道傳輸持續(xù)的數(shù)據(jù),就在通道位置用<font color="red">stream</font>修飾罢杉,一共有兩個(gè)位置趟畏,第一個(gè)是進(jìn)入服務(wù)端的入?yún)ⅲ诙€(gè)是從服務(wù)端出來的返回值滩租;
根據(jù)proto生成go源碼
- 在<font color="blue">grpcstream.proto</font>所在的目錄赋秀,執(zhí)行以下命令:
protoc --go_out=plugins=grpc:. grpcstream.proto
- 如果grpcstream.proto沒有語法錯(cuò)誤,會(huì)在當(dāng)前目錄生成文件<font color="blue">grpcstream.pb.go</font>律想,這里面是工具protoc-gen-go自動(dòng)生成的代碼沃琅,里面生成的代碼在開發(fā)服務(wù)端和客戶端時(shí)都會(huì)用到;
- 對(duì)服務(wù)端來說蜘欲,<font color="blue">grpcstream.pb.go</font>中最重要的是<font color="blue">IGrpcStremServiceServer</font>接口 益眉,服務(wù)端需要實(shí)現(xiàn)該接口所有的方法作為業(yè)務(wù)邏輯,接口定義如下:
type IGrpcStremServiceServer interface {
// 單項(xiàng)流式 :?jiǎn)蝹€(gè)請(qǐng)求姥份,單個(gè)響應(yīng)
SingleReqSingleResp(context.Context, *SingleRequest) (*SingleResponse, error)
// 服務(wù)端流式 :?jiǎn)蝹€(gè)請(qǐng)求郭脂,集合響應(yīng)
SingleReqMultiResp(*SingleRequest, IGrpcStremService_SingleReqMultiRespServer) error
// 客戶端流式 :集合請(qǐng)求,單個(gè)響應(yīng)
MultiReqSingleResp(IGrpcStremService_MultiReqSingleRespServer) error
// 雙向流式 :集合請(qǐng)求澈歉,集合響應(yīng)
MultiReqMultiResp(IGrpcStremService_MultiReqMultiRespServer) error
}
- 對(duì)客戶端來說展鸡,<font color="blue">grpcstream.pb.go</font>中最重要的是<font color="blue">IGrpcStremServiceClient</font>接口,如下所示埃难,這意味這客戶端可以發(fā)起哪些遠(yuǎn)程調(diào)用 :
type IGrpcStremServiceClient interface {
// 單項(xiàng)流式 :?jiǎn)蝹€(gè)請(qǐng)求莹弊,單個(gè)響應(yīng)
SingleReqSingleResp(ctx context.Context, in *SingleRequest, opts ...grpc.CallOption) (*SingleResponse, error)
// 服務(wù)端流式 :?jiǎn)蝹€(gè)請(qǐng)求,集合響應(yīng)
SingleReqMultiResp(ctx context.Context, in *SingleRequest, opts ...grpc.CallOption) (IGrpcStremService_SingleReqMultiRespClient, error)
// 客戶端流式 :集合請(qǐng)求涡尘,單個(gè)響應(yīng)
MultiReqSingleResp(ctx context.Context, opts ...grpc.CallOption) (IGrpcStremService_MultiReqSingleRespClient, error)
// 雙向流式 :集合請(qǐng)求忍弛,集合響應(yīng)
MultiReqMultiResp(ctx context.Context, opts ...grpc.CallOption) (IGrpcStremService_MultiReqMultiRespClient, error)
}
編寫服務(wù)端代碼server.go并啟動(dòng)
- 在<font color="blue">$GOPATH/src/grpcstream</font>目錄下新建文件夾<font color="red">server</font>,在此文件夾下新建<font color="red">server.go</font>考抄,內(nèi)容如下(稍后會(huì)指出幾處要注意的地方):
package main
import (
"context"
"google.golang.org/grpc"
pb "grpcstream"
"io"
"log"
"net"
"strconv"
)
// 常量:監(jiān)聽端口
const (
port = ":50051"
)
// 定義結(jié)構(gòu)體细疚,在調(diào)用注冊(cè)api的時(shí)候作為入?yún)ⅲ?// 該結(jié)構(gòu)體會(huì)帶上proto中定義的方法,里面是業(yè)務(wù)代碼
// 這樣遠(yuǎn)程調(diào)用時(shí)就執(zhí)行了業(yè)務(wù)代碼了
type server struct {
// pb.go中自動(dòng)生成的川梅,是個(gè)空結(jié)構(gòu)體
pb.UnimplementedIGrpcStremServiceServer
}
// 單項(xiàng)流式 :?jiǎn)蝹€(gè)請(qǐng)求疯兼,單個(gè)響應(yīng)
func (s *server) SingleReqSingleResp(ctx context.Context, req *pb.SingleRequest) (*pb.SingleResponse, error) {
id := req.GetId()
// 打印請(qǐng)求參數(shù)
log.Println("1. 收到請(qǐng)求:", id)
// 實(shí)例化結(jié)構(gòu)體SingleResponse然遏,作為返回值
return &pb.SingleResponse{Id: id, Name: "1. name-" + strconv.Itoa(int(id))}, nil
}
// 服務(wù)端流式 :?jiǎn)蝹€(gè)請(qǐng)求,集合響應(yīng)
func (s *server) SingleReqMultiResp(req *pb.SingleRequest, stream pb.IGrpcStremService_SingleReqMultiRespServer) error {
// 取得請(qǐng)求參數(shù)
id := req.GetId()
// 打印請(qǐng)求參數(shù)
log.Println("2. 收到請(qǐng)求:", id)
// 返回多條記錄
for i := 0; i < 10; i++ {
stream.Send(&pb.SingleResponse{Id: int32(i), Name: "2. name-" + strconv.Itoa(i)})
}
return nil
}
// 客戶端流式 :集合請(qǐng)求吧彪,單個(gè)響應(yīng)
func (s *server) MultiReqSingleResp(reqStream pb.IGrpcStremService_MultiReqSingleRespServer) error {
var addVal int32 = 0
// 在for循環(huán)中接收流式請(qǐng)求
for {
// 一次接受一條記錄
singleRequest, err := reqStream.Recv()
// 不等于io.EOF表示這是條有效記錄
if err == io.EOF {
log.Println("3. 客戶端發(fā)送完畢")
break
} else if err != nil {
log.Fatalln("3. 接收時(shí)發(fā)生異常", err)
break
} else {
log.Println("3. 收到請(qǐng)求:", singleRequest.GetId())
// 收完之后待侵,執(zhí)行SendAndClose返回?cái)?shù)據(jù)并結(jié)束本次調(diào)用
addVal += singleRequest.GetId()
}
}
return reqStream.SendAndClose(&pb.SingleResponse{Id: addVal, Name: "3. name-" + strconv.Itoa(int(addVal))})
}
// 雙向流式 :集合請(qǐng)求,集合響應(yīng)
func (s *server) MultiReqMultiResp(reqStream pb.IGrpcStremService_MultiReqMultiRespServer) error {
// 簡(jiǎn)單處理姨裸,對(duì)于收到的每一條記錄都返回一個(gè)響應(yīng)
for {
singleRequest, err := reqStream.Recv()
// 不等于io.EOS表示這是條有效記錄
if err == io.EOF {
log.Println("4. 接收完畢")
return nil
} else if err != nil {
log.Fatalln("4. 接收時(shí)發(fā)生異常", err)
return err
} else {
log.Println("4. 接收到數(shù)據(jù)", singleRequest.GetId())
id := singleRequest.GetId()
if sendErr := reqStream.Send(&pb.SingleResponse{Id: id, Name: "4. name-" + strconv.Itoa(int(id))}); sendErr != nil {
log.Println("4. 返回?cái)?shù)據(jù)異常數(shù)據(jù)", sendErr)
return sendErr
}
}
}
}
func main() {
// 要監(jiān)聽的協(xié)議和端口
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// 實(shí)例化gRPC server結(jié)構(gòu)體
s := grpc.NewServer()
// 服務(wù)注冊(cè)
pb.RegisterIGrpcStremServiceServer(s, &server{})
log.Println("開始監(jiān)聽秧倾,等待遠(yuǎn)程調(diào)用...")
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
- 這個(gè)<font color="blue">server.go</font>文件有以下幾處要注意的地方:
- SingleReqMultiResp方法的作用是收到客戶端一個(gè)請(qǐng)求參數(shù),然后向客戶端發(fā)送多個(gè)響應(yīng)啦扬,可見多次調(diào)用<font color="blue">stream.Send</font>方法即可多次發(fā)送數(shù)據(jù)到客戶端;
- MultiReqSingleResp方法可以從客戶端收到多條數(shù)據(jù)凫碌,可見是在for循環(huán)中重復(fù)調(diào)用<font color="blue">reqStream.Recv()</font>方法扑毡,直到收到客戶端的<font color="red">io.EOF</font>為止,這就要就客戶端在發(fā)送完數(shù)據(jù)后再給一個(gè)<font color="red">io.EOF</font>過來盛险,稍后的客戶端代碼會(huì)展示如何做瞄摊;
- MultiReqMultiResp方法持續(xù)接受客戶端數(shù)據(jù),并且持續(xù)發(fā)送數(shù)據(jù)給客戶端苦掘,一定要把順序問題考慮清楚换帜,否則會(huì)陷入異常(例如一方死循環(huán)),我這里的邏輯是收到客戶端的<font color="red">io.EOF</font>為止鹤啡,這就要就客戶端在發(fā)送完數(shù)據(jù)后再給一個(gè)<font color="red">io.EOF</font>過來惯驼,如果客戶端也在用for循環(huán)一直等數(shù)據(jù),那就是雙方都在等數(shù)據(jù)了递瑰,無法終止程序祟牲,稍后的客戶端代碼會(huì)展示如何做;
- 在server.go所在目錄執(zhí)行<font color="blue">go run server.go</font>抖部,控制臺(tái)提示如下:
[golang@centos7 server]$ go run server.go
2020/12/13 21:29:19 開始監(jiān)聽说贝,等待遠(yuǎn)程調(diào)用...
- 此時(shí)gRPC的服務(wù)端已經(jīng)啟動(dòng),可以響應(yīng)遠(yuǎn)程調(diào)用慎颗,接下來開發(fā)客戶端代碼乡恕;
編寫客戶端代碼client.go
- 再打開一個(gè)控制臺(tái);
- 在<font color="blue">$GOPATH/src/grpcstream</font>目錄下新建文件夾<font color="red">client</font>俯萎,在此文件夾下新建<font color="red">client.go</font>傲宜,內(nèi)容如下(稍后會(huì)指出幾處要注意的地方):
package main
import (
"context"
"google.golang.org/grpc"
"io"
"log"
"time"
pb "grpcstream"
)
const (
address = "localhost:50051"
defaultId = "666"
)
func main() {
// 遠(yuǎn)程連接服務(wù)端
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
// main方法執(zhí)行完畢后關(guān)閉遠(yuǎn)程連接
defer conn.Close()
// 實(shí)例化數(shù)據(jù)結(jié)構(gòu)
client := pb.NewIGrpcStremServiceClient(conn)
// 超時(shí)設(shè)置
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
log.Println("測(cè)試單一請(qǐng)求應(yīng)答,一對(duì)一")
singleReqSingleResp(ctx, client)
log.Println("測(cè)試服務(wù)端流式應(yīng)答夫啊,一對(duì)多")
singleReqMultiResp(ctx, client)
log.Println("測(cè)試客戶端流式請(qǐng)求蛋哭,多對(duì)一")
multiReqSingleResp(ctx, client)
log.Println("測(cè)試雙向流式請(qǐng)求應(yīng)答,多對(duì)多")
multiReqMultiResp(ctx, client)
log.Println("測(cè)試完成")
}
func singleReqSingleResp(ctx context.Context, client pb.IGrpcStremServiceClient) error {
// 遠(yuǎn)程調(diào)用
r, err := client.SingleReqSingleResp(ctx, &pb.SingleRequest{Id: 101})
if err != nil {
log.Fatalf("1. 遠(yuǎn)程調(diào)用異常 : %v", err)
return err
}
// 將服務(wù)端的返回信息打印出來
log.Printf("response, id : %d, name : %s", r.GetId(), r.GetName())
return nil
}
func singleReqMultiResp(ctx context.Context, client pb.IGrpcStremServiceClient) error {
// 遠(yuǎn)程調(diào)用
recvStream, err := client.SingleReqMultiResp(ctx, &pb.SingleRequest{Id: 201})
if err != nil {
log.Fatalf("2. 遠(yuǎn)程調(diào)用異常 : %v", err)
return err
}
for {
singleResponse, err := recvStream.Recv()
if err == io.EOF {
log.Printf("2. 獲取數(shù)據(jù)完畢")
break
}
log.Printf("2. 收到服務(wù)端響應(yīng), id : %d, name : %s", singleResponse.GetId(), singleResponse.GetName())
}
return nil
}
func multiReqSingleResp(ctx context.Context, client pb.IGrpcStremServiceClient) error {
// 遠(yuǎn)程調(diào)用
sendStream, err := client.MultiReqSingleResp(ctx)
if err != nil {
log.Fatalf("3. 遠(yuǎn)程調(diào)用異常 : %v", err)
return err
}
// 發(fā)送多條記錄到服務(wù)端
for i:=0; i<10; i++ {
if err = sendStream.Send(&pb.SingleRequest{Id: int32(300+i)}); err!=nil {
log.Fatalf("3. 通過流發(fā)送數(shù)據(jù)異常 : %v", err)
return err
}
}
singleResponse, err := sendStream.CloseAndRecv()
if err != nil {
log.Fatalf("3. 服務(wù)端響應(yīng)異常 : %v", err)
return err
}
// 將服務(wù)端的返回信息打印出來
log.Printf("response, id : %d, name : %s", singleResponse.GetId(), singleResponse.GetName())
return nil
}
func multiReqMultiResp(ctx context.Context, client pb.IGrpcStremServiceClient) error {
// 遠(yuǎn)程調(diào)用
intOutStream, err := client.MultiReqMultiResp(ctx)
if err != nil {
log.Fatalf("4. 遠(yuǎn)程調(diào)用異常 : %v", err)
return err
}
// 發(fā)送多條記錄到服務(wù)端
for i:=0; i<10; i++ {
if err = intOutStream.Send(&pb.SingleRequest{Id: int32(400+i)}); err!=nil {
log.Fatalf("4. 通過流發(fā)送數(shù)據(jù)異常 : %v", err)
return err
}
}
// 服務(wù)端一直在接收涮母,直到收到io.EOF為止
// 因此谆趾,這里必須發(fā)送io.EOF到服務(wù)端躁愿,讓服務(wù)端知道發(fā)送已經(jīng)結(jié)束(很重要)
intOutStream.CloseSend()
// 接收服務(wù)端發(fā)來的數(shù)據(jù)
for {
singleResponse, err := intOutStream.Recv()
if err == io.EOF {
log.Printf("4. 獲取數(shù)據(jù)完畢")
break
} else if err != nil {
log.Fatalf("4. 接收服務(wù)端數(shù)據(jù)異常 : %v", err)
break
}
log.Printf("4. 收到服務(wù)端響應(yīng), id : %d, name : %s", singleResponse.GetId(), singleResponse.GetName())
}
return nil
}
- 這個(gè)<font color="blue">client.go</font>文件有以下幾處要注意的地方:
- singleReqMultiResp方法會(huì)接收服務(wù)端的多條記錄,在for循環(huán)中調(diào)用<font color="blue">recvStream.Recv</font>即可收到所有數(shù)據(jù)沪蓬;
- multiReqSingleResp方法會(huì)向服務(wù)端發(fā)送多條數(shù)據(jù)彤钟,由于服務(wù)端在等待<font color="red">io.EOF</font>作為結(jié)束標(biāo)志,因此調(diào)用<font color="blue">sendStream.CloseAndRecv</font>即可發(fā)送<font color="red">io.EOF</font>跷叉,并得到服務(wù)端的返回值逸雹;
- multiReqMultiResp方法在持續(xù)向服務(wù)端發(fā)送數(shù)據(jù),并且也在持續(xù)獲取服務(wù)端發(fā)來的數(shù)據(jù)云挟,在發(fā)送數(shù)據(jù)完成后梆砸,必須調(diào)用<font color="blue">intOutStream.CloseSend</font>方法,即可發(fā)送<font color="red">io.EOF</font>园欣,讓服務(wù)端不再接收數(shù)據(jù)帖世,避免前面提到的死循環(huán);
- 在main方法中沸枯,依次發(fā)起四類服務(wù)方法的調(diào)用日矫;
執(zhí)行客戶端
- 編碼完成后,在client.go所在目錄執(zhí)行<font color="blue">go run client.go</font>绑榴,會(huì)立即向服務(wù)端發(fā)起遠(yuǎn)程調(diào)用哪轿,控制臺(tái)提示如下,可見四類服務(wù)方法測(cè)試全部成功翔怎,響應(yīng)的數(shù)據(jù)都符合預(yù)期:
[golang@centos7 client]$ go run client.go
2020/12/13 21:39:35 測(cè)試單一請(qǐng)求應(yīng)答窃诉,一對(duì)一
2020/12/13 21:39:35 response, id : 101, name : 1. name-101
2020/12/13 21:39:35 測(cè)試服務(wù)端流式應(yīng)答,一對(duì)多
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 0, name : 2. name-0
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 1, name : 2. name-1
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 2, name : 2. name-2
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 3, name : 2. name-3
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 4, name : 2. name-4
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 5, name : 2. name-5
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 6, name : 2. name-6
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 7, name : 2. name-7
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 8, name : 2. name-8
2020/12/13 21:39:35 2. 收到服務(wù)端響應(yīng), id : 9, name : 2. name-9
2020/12/13 21:39:35 2. 獲取數(shù)據(jù)完畢
2020/12/13 21:39:35 測(cè)試客戶端流式請(qǐng)求赤套,多對(duì)一
2020/12/13 21:39:35 response, id : 3045, name : 3. name-3045
2020/12/13 21:39:35 測(cè)試雙向流式請(qǐng)求應(yīng)答褐奴,多對(duì)多
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 400, name : 4. name-400
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 401, name : 4. name-401
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 402, name : 4. name-402
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 403, name : 4. name-403
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 404, name : 4. name-404
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 405, name : 4. name-405
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 406, name : 4. name-406
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 407, name : 4. name-407
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 408, name : 4. name-408
2020/12/13 21:39:35 4. 收到服務(wù)端響應(yīng), id : 409, name : 4. name-409
2020/12/13 21:39:35 4. 獲取數(shù)據(jù)完畢
2020/12/13 21:39:35 測(cè)試完成
- 再去服務(wù)端的控制臺(tái)看一下,通過日志發(fā)現(xiàn)業(yè)務(wù)代碼被執(zhí)行于毙,收到了遠(yuǎn)程請(qǐng)求的參數(shù):
[golang@centos7 server]$ go run server.go
2020/12/13 21:29:19 開始監(jiān)聽敦冬,等待遠(yuǎn)程調(diào)用...
2020/12/13 21:39:35 1. 收到請(qǐng)求: 101
2020/12/13 21:39:35 2. 收到請(qǐng)求: 201
2020/12/13 21:39:35 3. 收到請(qǐng)求: 300
2020/12/13 21:39:35 3. 收到請(qǐng)求: 301
2020/12/13 21:39:35 3. 收到請(qǐng)求: 302
2020/12/13 21:39:35 3. 收到請(qǐng)求: 303
2020/12/13 21:39:35 3. 收到請(qǐng)求: 304
2020/12/13 21:39:35 3. 收到請(qǐng)求: 305
2020/12/13 21:39:35 3. 收到請(qǐng)求: 306
2020/12/13 21:39:35 3. 收到請(qǐng)求: 307
2020/12/13 21:39:35 3. 收到請(qǐng)求: 308
2020/12/13 21:39:35 3. 收到請(qǐng)求: 309
2020/12/13 21:39:35 3. 客戶端發(fā)送完畢
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 400
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 401
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 402
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 403
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 404
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 405
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 406
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 407
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 408
2020/12/13 21:39:35 4. 接收到數(shù)據(jù) 409
2020/12/13 21:39:35 4. 接收完畢
- 至此,gRPC的四類服務(wù)方法的服務(wù)端唯沮、客戶端開發(fā)咱們都嘗試過了脖旱,這四類方法已經(jīng)可以覆蓋了大多數(shù)業(yè)務(wù)場(chǎng)景需求,希望本文能給您一些參考介蛉,接下來的文章會(huì)繼續(xù)學(xué)習(xí)gRPC豐富的功能萌庆;
你不孤單,欣宸原創(chuàng)一路相伴
歡迎關(guān)注公眾號(hào):程序員欣宸
微信搜索「程序員欣宸」币旧,我是欣宸践险,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos