gRPC 應用指引

2023-04-25 15:03:13

一、核心概念、架構及生命週期

1、服務定義

gRPC 預設使用 protocol buffers

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}

gRPC 可以定義四種型別服務:

  • Unary RPCs:一次請求,一次回覆。

    rpc SayHello(HelloRequest) returns (HelloResponse);
    
  • 伺服器端流式請求:使用者端傳送一次請求,伺服器端流式返回一系列資料。

    rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
    
  • 使用者端流式請求:使用者端流式寫入一系列請求,然後傳送到伺服器端。使用者端寫完請求後,等待伺服器端接受並返回結果。

    rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
    
  • 雙向流式請求:使用者端和伺服器端雙向傳送資料流,各自獨立。可以隨讀隨寫,或者一次性讀完再寫。

    rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
    

2、API 使用

首先在 .proto 檔案中定義一個服務,然後使用 gRPC 提供的 pb 編譯外掛來生成使用者端和伺服器端程式碼。

  • 伺服器端:實現定義的服務,響應使用者端請求。gRPC 框架解碼請求,執行服務方法,編碼返回結果。
  • 使用者端:本地 stub 包含實現的服務方法,使用者端可以直接呼叫 stub 的相應方法,以 pb 訊息型別包裝請求引數傳送到伺服器,同時返回伺服器返回的結果。

3、同步和非同步

同步 RPC 請求(傳送請求,阻塞直到伺服器端返回結果)和我們通常所說的 RPC 定義最為接近。但是,在實際應用中,非阻塞非同步請求更適合。

4、RPC 生命週期

a)Unary RPC

使用者端傳送一次請求,獲取一次返回。

  • 使用者端請求本地 stub 方法,伺服器端獲取到通知,並伴隨著使用者端的請求資料,包括使用者端 metadata、方法名及 deadline。伺服器端可以直接返回自身的 metadata(必須在業務結果返回前返回)或者等待使用者端的請求訊息(自定義)。
  • 伺服器端收到使用者端請求訊息,然後執行相應的方法,組裝相應的資料結果,伴隨著請求狀態資訊(狀態碼及可能狀態訊息)返回給使用者端。
  • 如果狀態為 OK,則使用者端可以獲取到結果進行處理,完成整個呼叫過程。

b)伺服器端流式 RPC

伺服器端返回的是一個資料流。在伺服器端傳送完業務資料後,會繼續返回狀態資訊。

c)使用者端流式 RPC

使用者端傳送的是一個請求資料流。

d)雙向流式 RPC

使用者端和伺服器端雙向傳送資料流,各自獨立。可以隨讀隨寫,或者一次性讀完再寫。

5、Deadlines/Timeouts

gRPC 允許使用者端宣告超時(請求 DEADLINE_EXCEEDED 異常之前等待的時間)。伺服器端可以通過此來判定請求是否超時及剩餘處理時間。

6、RPC 終止

gRPC 中使用者端和伺服器端都可以獨立終止請求。比如伺服器端已經成功響應請求,但是使用者端超時終止;伺服器端在接收完使用者端請求資料前限頻校驗終止請求流程。

7、RPC 請求取消

使用者端和伺服器端都可以在任何時候取消 RPC 流程。

8、Metadata

RPC 請求後設資料,kv 列表形式,key 為 string 型別,value 通常為string,也可以為二進位制。

key 大小寫敏感,不能以 grpc- 做字首(保留),二進位制 value 的 key 以 -bin 結尾。

gRPC 不會使用使用者自定義的後設資料。

後設資料使用,不同開發語言可能不同。

9、Channels

gRPC channel 是使用者端到伺服器端的連結。用以建立使用者端 stub。

channel 提供相應的引數設定控制 gRPC 請求行為,例如互動資料壓縮等。

channel 的狀態包括已建立連結及空閒。 

二、最佳實踐

rpc 請求初始化包括:使用者端負載均衡,傳輸層 HTTP/2 請求建立及請求伺服器端相應的業務介面。

儘量重用 stubs 和 channels。

2、提供心跳機制以確保 HTTP/2 連線即使在系統業務不活躍時段仍能保持活躍,避免因 RPC 請求初始化導致的響應延遲。

3、對於可能存續長時間的資料流請求互動,適宜使用流式處理,避免頻繁的 RPC 初始化。但是流式處理也存在無法動態均衡負載的及debug 困難的問題。雖然可以在小規模請求上提升效能,但是會因為負載均衡因素及複雜性降低整體擴充套件性。(python 除外)

4、每一個 gRPC channel 可以使用 0 個或多個 HTTP/2 連結,每個連結可以承載一定數量的的並行資料流。當連結上活躍的 RPC 請求達到上限,新進的請求會進入呼叫端等待佇列。因此,對於高負載或持久的流式請求會因此產生效能問題。對於此,可以使用如下兩種方式處理:

  • 對於此類業務請求使用額外的 chennel。
  • 使用 gRPC 連線池來均衡處理請求(需要特定的處理來避免重複使用同一個 channel) 

5、對於 Java 語言

  • 使用非阻塞 stubs 來並行處理 RPC 請求。

  • 提供自定義連線池,根據實際的業務負載來設定相關引數。