gRPC學(xué)習(xí)之四:實(shí)戰(zhàn)四類服務(wù)方法

歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內(nèi)容:所有原創(chuàng)文章分類匯總及配套源碼拐纱,涉及Java、Docker、Kubernetes赢底、DevOPS等躯畴;

gRPC學(xué)習(xí)系列文章鏈接

  1. 在CentOS7部署和設(shè)置GO
  2. GO的gRPC開發(fā)環(huán)境準(zhǔn)備
  3. 初試GO版gRPC開發(fā)
  4. 實(shí)戰(zhàn)四類服務(wù)方法
  5. gRPC-Gateway實(shí)戰(zhàn)
  6. 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>):
  1. 單項(xiàng) RPC暮屡,即客戶端發(fā)送一個(gè)請(qǐng)求給服務(wù)端,從服務(wù)端獲取一個(gè)應(yīng)答毅桃,就像一次普通的函數(shù)調(diào)用(前一篇文章就是此類)褒纲;
  2. 服務(wù)端流式 RPC愁溜,即客戶端發(fā)送一個(gè)請(qǐng)求給服務(wù)端,可獲取一個(gè)數(shù)據(jù)流用來讀取一系列消息外厂。客戶端從返回的數(shù)據(jù)流里一直讀取直到?jīng)]有更多消息為止代承;
  3. 客戶端流式 RPC汁蝶,即客戶端用提供的一個(gè)數(shù)據(jù)流寫入并發(fā)送一系列消息給服務(wù)端。一旦客戶端完成消息寫入论悴,就等待服務(wù)端讀取這些消息并返回應(yīng)答掖棉;
  4. 雙向流式 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ā)流程如下圖所示:
在這里插入圖片描述

源碼下載

名稱 鏈接 備注
項(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>文件有以下幾處要注意的地方:
  1. 方法<font color="blue">SingleReqSingleResp</font>非常簡(jiǎn)單既棺,和上一篇文章中的demo一樣,入?yún)⑹且粋€(gè)數(shù)據(jù)結(jié)構(gòu)懒叛,服務(wù)端返回的也是一個(gè)數(shù)據(jù)結(jié)構(gòu)丸冕;
  2. 方法<font color="blue">SingleReqSingleResp</font>是服務(wù)端流式類型,特征是返回值用<font color="red">stream</font>修飾薛窥;
  3. 方法<font color="blue">MultiReqSingleResp</font>是客戶端流式類型胖烛,特征是入?yún)⒂?lt;font color="red">stream</font>修飾;
  4. 方法<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源碼

  1. 在<font color="blue">grpcstream.proto</font>所在的目錄赋秀,執(zhí)行以下命令:
protoc --go_out=plugins=grpc:. grpcstream.proto
  1. 如果grpcstream.proto沒有語法錯(cuò)誤,會(huì)在當(dāng)前目錄生成文件<font color="blue">grpcstream.pb.go</font>律想,這里面是工具protoc-gen-go自動(dòng)生成的代碼沃琅,里面生成的代碼在開發(fā)服務(wù)端和客戶端時(shí)都會(huì)用到;
  2. 對(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
}
  1. 對(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>文件有以下幾處要注意的地方:
  1. SingleReqMultiResp方法的作用是收到客戶端一個(gè)請(qǐng)求參數(shù),然后向客戶端發(fā)送多個(gè)響應(yīng)啦扬,可見多次調(diào)用<font color="blue">stream.Send</font>方法即可多次發(fā)送數(shù)據(jù)到客戶端;
  2. 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ì)展示如何做瞄摊;
  3. 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>文件有以下幾處要注意的地方:
  1. singleReqMultiResp方法會(huì)接收服務(wù)端的多條記錄,在for循環(huán)中調(diào)用<font color="blue">recvStream.Recv</font>即可收到所有數(shù)據(jù)沪蓬;
  2. 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ù)端的返回值逸雹;
  3. 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);
  4. 在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)一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 數(shù)據(jù)庫(kù)+中間件系列
  6. DevOps系列

歡迎關(guān)注公眾號(hào):程序員欣宸

微信搜索「程序員欣宸」币旧,我是欣宸践险,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子巍虫,更是在濱河造成了極大的恐慌彭则,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件占遥,死亡現(xiàn)場(chǎng)離奇詭異俯抖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瓦胎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門芬萍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搔啊,你說我怎么就攤上這事柬祠。” “怎么了负芋?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵漫蛔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我示罗,道長(zhǎng)惩猫,這世上最難降的妖魔是什么芝硬? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任蚜点,我火速辦了婚禮,結(jié)果婚禮上拌阴,老公的妹妹穿的比我還像新娘绍绘。我一直安慰自己,他們只是感情好迟赃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布陪拘。 她就那樣靜靜地躺著,像睡著了一般纤壁。 火紅的嫁衣襯著肌膚如雪左刽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天酌媒,我揣著相機(jī)與錄音欠痴,去河邊找鬼。 笑死秒咨,一個(gè)胖子當(dāng)著我的面吹牛喇辽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雨席,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼菩咨,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抽米,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤特占,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后缨硝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摩钙,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年查辩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胖笛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卜壕,到底是詐尸還是另有隱情剃氧,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布丸边,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏阱佛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一戴而、第九天 我趴在偏房一處隱蔽的房頂上張望凑术。 院中可真熱鬧,春花似錦所意、人聲如沸淮逊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泄鹏。三九已至,卻和暖如春秧耗,著一層夾襖步出監(jiān)牢的瞬間备籽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工分井, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留车猬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓杂抽,卻偏偏與公主長(zhǎng)得像诈唬,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缩麸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容