gRPC學習筆記

介紹(what

gRPC 是一個高性能、開源和通用的 RPC(遠程過程調用協議) 框架。主要思想是:定義一個服務, 指定其可以被遠程調用的方法及其參數和返回類型。用來實現不同進程間的通信。
grpc實現和調用過程

上圖爲gRPC工作流程圖,主要從兩個方面來理解該流程.
1.圖中服務端使用的是C++語言開發的系統,客戶端分別使用的是Android-Java開發的平臺和Ruby語言開發的平臺.gRPC可以在不同的語言開發的平臺上使用.
2.主要實現的流程爲,在服務端實現自身定義的服務內的接口,並運行一個 gRPC 服務器來處理客戶端的請求調用並將處理結果返回到客戶端。在客戶端擁有一個存根能夠像服務端一樣的方法。

使用(how)

具體怎麼實現gRPC的工作流程呢?(How),實現之前需要先了解gRPC 默認使用 protocol buffers接口定義語言(IDL)可以類比AIDL--Android平臺上的接口定義語言,也是用來定義不同進程間通信的接口.
step 1————–在一個 .proto 文件內定義服務。

//此文件爲Hello.proto(類似於Android中的*.aidl文件)
syntax = "proto3";
//可以定義四種service,請求的消息類型和返回的消息類型一般是在此文件中定義message.
service IFourKind{
//第一種service,一個 簡單 RPC,客戶端使用存根發送請求到服務器並等待響應返回,就像平常的函數調用一樣。
 rpc GetARespone(ARequest) returns (ARespone) {}

//第二種service,一個服務器端流式RPC,客戶端發送請求到服務器,拿到一個流去讀取返回的消息序列。 客戶端讀取返回的流,直到裏面沒有任何消息。
 rpc ListBRespone(ARequest) returns (stream ARespone) {}

 //第三種service,一個客戶端流式RPC,客戶端寫入一個消息序列並將其發送到服務器,同樣也是使用流。一旦 客戶端完成寫入消息,它等待服務器完成讀取返回它的響應。
 rpc ListCRespone(stream ARequest) returns (ARespone) {}

 //第四種service,一個雙向流式 RPC 是雙方使用讀寫流去發送一個消息序列。兩個流獨立操作,因此客戶端和服務器 可以以任意喜歡的順序讀寫
rpc ListDRespone(stream ARequest) returns (stream ARespone) {}

//請求類型
message ARequest{
...//成員變量,參考protocol buffers數據格式的定義
}
//迴應類型
message ARespone{
...//成員變量,參考protocol buffers數據格式的定義
}

step 2————–用 protocol buffer 編譯器生成指定編程語言的服務器和客戶端代碼。

通過 protocol buffer 的編譯器 protoc 或者一個protoc的IDEA插件來完成。生成.proto文件中定義的接口類和message消息類型的類文件.

step 3————–使用 gRPC 的 對應的語言的 API 實現服務端和客戶端的進程間的通信.

服務端的主要工作
1.實現定義的服務接口函數.
2.開啓服務端,監聽來自客戶端的請求並響應客戶端的請求.

//1.實現定義的服務IFourKind接口函數.
///////////第一種service
 @Override
 public void GetARespone(ARequest request,StreamObserver<ARespone> responseObserver) {
 //ARequest爲客戶端請求的消息,StreamObserver<ARespone>爲服務端響應客戶端的接口
      ARespone response=........(省略的代碼爲根據客戶端請求做出處理,生成迴應的類型的對象)
      responseObserver.onNext(response);//返回給客戶端,具體看怎麼返回需要查看源碼
      responseObserver.onCompleted();//服務端完成客戶端請求迴應的回調函數.
    }
///////////第二種service同第一種
 @Override
 public void ListBRespone(ARequest request, StreamObserver<ARespone> responseObserver) {
   //ARequest爲客戶端請求的消息,StreamObserver<ARespone>爲服務端響應客戶端的接口
      ARespone response=........(省略的代碼爲根據客戶端請求做出處理,生成迴應的類型的對象)
      responseObserver.onNext(response);//返回給客戶端,具體看怎麼返回需要查看源碼
      responseObserver.onCompleted();//服務端完成客戶端請求迴應的回調函數.
      responseObserver.onCompleted();
    }
 ///////////第三種service
 @Override
 public StreamObserver<ARequest> ListCRespone(final StreamObserver<ARespone> responseObserver) {
      return new StreamObserver<ARequest>() {    
        @Override
        public void onNext(ARequest request) {
         //從客戶端不斷接受ARequest流
         //每接收到可以對ARequest進行處理
         ..........
        }      
        @Override
        public void onError(Throwable t) {
          logger.log("error------");
        }
        //客戶端發送請求流結束
        @Override
        public void onCompleted() {
         ARespone response=........(省略的代碼爲根據客戶端請求做出處理,生成迴應的類型的對象)
          responseObserver.onNext(response);                   
          responseObserver.onCompleted();
        }
      };
    }
 ///////////第四種service
 @Override
 public StreamObserver<ARequest> ListDRespone(final StreamObserver<ARespone> responseObserver) {
      return new StreamObserver<ARequest>() {   
        @Override
        public void onNext(ARequest request) {
         //從客戶端不斷接受ARequest流
         //每接收到可以對ARequest進行處理,.
         //處理完可以立馬responseObserver.onNext(response)迴應給客戶端;  
         ..........
        }    
        @Override
        public void onError(Throwable t) {
          logger.log("error------");
        }
        //客戶端發送請求流結束
        @Override
        public void onCompleted() {
         ARespone response=........(省略的代碼爲根據客戶端請求做出處理,生成迴應的類型的對象)
          responseObserver.onNext(response);                   
          responseObserver.onCompleted();
        }
      };
    }
--------------------------------------------------------------------------   
//2.開啓服務端,監聽來自客戶端的請求並響應客戶端的請求.
public void start() {
//綁定端口,添加service,啓動服務
    gRpcServer = NettyServerBuilder.forPort(port)
        .addService(IFourKindGrpc.bindService(new IFourKindService()))
        .build().start();
    logger.info("Server started, listening on " + port);
    ...
  }

客戶端的主要工作
1.創建Client實例並連接 server.
2.調用服務端方法(請求)並獲取服務端迴應.

//1.創建gRPC channel,根據端口和IP連接服務端
channel = NettyChannelBuilder.forAddress(host, port)
        .negotiationType(NegotiationType.PLAINTEXT)
        .build();
//2.創建存根,存根是根據.proto文件中生成的類IFourKindGrpc的代理.blockingStub爲阻塞式需要阻塞等待服務端的迴應,而asyncStub爲非阻塞可以異步執行.
  blockingStub = RouteGuideGrpc.newBlockingStub(channel);
    asyncStub = RouteGuideGrpc.newStub(channel); 
//3.調用服務端的服務方法.相當於發送請求,並獲取服務端的迴應.
////調用服務端的第一種service 
   ARequest request = .....//省略了構造請求對象的代碼
   ARespone respone = blockingStub.GetARespone(request); //獲取服務端迴應 

////調用服務端的第二種service 
   ARequest request = .....//省略了構造請求對象的代碼
   ARespone respone = blockingStub.ListBRespone(request); //獲取服務端迴應 

////調用服務端的第三種service 
//a.構造接收服務端迴應的操作接口.
 StreamObserver<ARespone> responseObserver = new StreamObserver<ARespone>() {
      @Override
      public void onNext(ARespone summary) {
       //接收到迴應的處理do something
      }
      @Override
      public void onError(Throwable t) {
       //do something
      }
      @Override
      public void onCompleted() {
       //do something
      }
    };
//b.客戶端創建請求對象的流操作接口.
StreamObserver<ARespone> requestObserver = asyncStub.ListCRespone(responseObserver); 
//c.創建請求對象
ARequest request = .....//省略了構造請求對象的代碼
//d.客戶端發送請求流
requestObserver.onNext(request);   
//e.完成請求
requestObserver.onCompleted();

////調用服務端的第四種service ,方式同第三種

總結

1.本文主要講的是gRPC的使用過程的細節,使用的是Java語言的例子.本文並沒有深究自定義服務文件通過protoc自動生成的指定語言的類的代碼.如果想了解服務端和客戶端之間詳細的回調過程還需要詳細查閱自動生成的類RouteGuideGrpc裏面的代碼.在瞭解了該類的詳細過程後,也可以不是有IDL文件來自動生成類,可以自己編寫相應的類來完成進程間的通信的服務類.此塊開發類似與Android開發中的不同進程的使用AIDL來通信.
2.PRC的服務端和客戶端間的通信基本問題還是不同進程間的通信,主要還是通過ip和端口號來標誌不同的進程.從而通信的時候能找到對應的進程.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章