GRPC從使用到深入--服務的定義和使用

一、使用GRPC需要考慮的問題

忽略底層細節,從使用RPC的角度,最主要的就是要定義一個方法簽名。這個方法,由服務端去實現,由客戶端去調用。

因此我們關心一下幾方面:

①方法的參數:決定了客戶端要請求的數據;

②方法的返回值:決定了服務端要返回的數據;

③方法的名稱:決定了RPC要實現什麼功能;

從這個角度去看GRPC的文檔,發現他的方法定義不太好理解。

我們先看一下官網給的案例:

syntax = "proto3";

service RouteGuide {

  // A simple RPC.
  rpc GetFeature(Point) returns (Feature) {}

  // A server-to-client streaming RPC.
  rpc ListFeatures(Rectangle) returns (stream Feature) {}

  // A client-to-server streaming RPC.
  rpc RecordRoute(stream Point) returns (RouteSummary) {}

  // A Bidirectional streaming RPC.
  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}

以上定義中

關鍵字rpc代表這是一個服務的定義

另外一個關鍵字 stream則是GRPC的重要特性。我們從客戶端和服務端通信的角度去理解它

最簡單的情況下:

這種情況很簡單,但是如果客戶端或者服務端之間的交互很頻繁時,每次方法調用都要建立連接,所以開銷比較大,GRPC考慮了這一點,所以需要再定義客戶端和服務端是否爲Stream。這四種定義方式帶來的影響,我們通過GRPC生成的代碼來分析。

1.1服務端代碼分析

public static abstract class RouteGuideIMplBase {

public void getFeature(Point request,StreamObserver<Feature> responseObserver) {}

public void listFeatures(Rectangle request,StreamObserver<Feature> responseObserver){}

public StreamObserver<Point> recordRoute(StreamObserver<RouteSummary>responseObserver){}

public treamObserver<RouteNote> routeChat(StreamObserver<RouteNote> responseObserver){}

}

這是需要我們繼承並實現的一個抽象類。從這四個方法簽名去看,發現雖然服務定義有四種類型,但是生成的代碼只有兩種簽名。

不管是否定義了Server-Stream,GRPC框架都爲我們提供了一個responseObserver, 這是一個Stream<ResponseType>,服務端用它來返回相應數據。

至於Client-Stream,纔是對方法簽名產生影響的唯一因素:

未定義爲Client-Stream: 這就是開頭說的最簡單的行式,客戶端將請求數據一次性地發送給服務端,服務端則直接處理完數據,然後通過responseObserver返回處理結果即可。

定義了Client-Stream: 這種情況下,服務端無法知道請求數據何時到達,也不知道請求數據何時結束。因此GRPC框架要求我們自己實現一個處理請求流數據的Observer對象,即方法返回的StreamObserver<RequestType>類型對象。 返回的整個Observer對象會交給GRPC框架去調用。

1.2 客戶端代碼分析

生成的客戶端代碼相對來說複雜一些,因爲GRPC爲客戶端生成了三種存根:

public static RouteGuideStub newStub(Channel channel) {
    return new RouteGuideStub(channel);
}

public static RouteGuideBlockingStub newBlockingStub(Channel channel) {
    return new RouteGuideBlockingStub(channel);
}

public static RouteGuideFutureStub newFutureStub(Channel channel) {
    return new RouteGuideFutureStub(channel);
}

這三種存根從名字也大概能猜出他們的功能

1.Stub:提供完全異步的方法

public static final class RouteGuideStub {

public void getFeature(Point request,StreamObserver<Feature> responseObserver){}

public void listFeatures(Rectangle request,StreamObserver<Feature> responseObserver){}

public StreamObserver<Point> recordRoute(StreamObserver<RouteSummary> responseObserver){}

public StreamObserver<RouteNote> routeChat(StreamObserver<RouteNote> responseObserver){}

}

客戶端要使用Stub,必須要先實現自己的responseObserver,用來處理服務端返回的消息。

前面也說了,服務端的實現裏面,響應數據都是以Stream的行式返回的,因此這部分很好理解。

區別仍然是Client-Stream部分。

1.未定義Client-Stream,則Stub直接將客戶端準備好的數據放到前兩個方法的參數中,然後調用方法

2.定義了Client-Stream,這說明Client無法一次性準備好請求數據,因此stub的後兩個方法調用後,GRPC框架會返回一個Stream Observer<RequestType>,這個對象供客戶端隨時調用,發送Stream行式的數據。

2.BlockStub :提供完全同步的方法

public static final class RouteGuideBlockingStub{

  public Feature getFeature(Point request){……}

  public Iterator<Feature> listFeatures(Rectangle request){……}

}

可以看到,BlockStub只實現了定義Client-Stream的服務,這也很好理解,既然是同步方法,那就要求一次調用就結束。

唯一需要說明的是,同步方法也實現了Server-Stream,其實BlockStub是等服務端把數據都返回後,再一次性處理的,如果服務端一直沒有完成響應數據的返回,那麼listFeatures()方法會一直阻塞等待。

3.FutrueStub

public static final class RouteGuideFutureStub{

 public ListenableFuture<Feature> getFeature(Point request) {}

}

可以看到,FutureStub只實現了一個方法,它其實就是解決了BlockStub在處理Server-Stream的方法時阻塞的問題

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