天天看點

玩轉gRPC—深入概念與原理

本篇文章屬于一篇知識的撿漏和複盤類的文章,主要目的就是為了複盤一下gRPC的相關概念,并剖析其原理,相關知識點和使用大家可以參看之前的幾篇文章:

  • ​​《玩轉gRPC—Go使用gRPC通信實戰》​​
  • ​​《玩轉gRPC—不同程式設計語言間通信》​​
  • ​​《一文帶你搞懂HTTP和RPC協定的異同》​​
  • ​​《從1開始,擴充Go語言後端業務系統的RPC功能》​​

以上的幾篇文章都不同程度的講述和RPC協定相關知識和Google開源的RPC架構gRPC的相關知識,但是可能都比較淺顯和不成體系,是以想利用這篇文章系統深入的講述下gRPC,下面開始:

1 使用gRPC的基本架構

玩轉gRPC—深入概念與原理

由上圖我們可以看出,使用gRPC通信的基本架構中基本分為五部分,他們分别是:

  • Service:提供的服務
  • Client:gRPC用戶端
  • gRPC Server:gRPC服務端接口
  • gRPC Stub:gRPC用戶端接口
  • Proto Request/Proto Response(s):中間檔案(代碼/協定)

2 Protocol Buffers

2.1 什麼是Protocol Buffers?

Protocol Buffers,是Google公司開發的一種資料描述語言,簡稱protobuf。

特點:

  • 支援多種程式設計語言
  • 序列化資料體積小
  • 反序列化速度快
  • 序列化和反序列化代碼自動生成

2.2 Protocol Buffers和gRPC什麼關系?

首先要說明的是gRPC是RPC協定是一種實作,是一個架構;Protocol Buffers,是Google公司開發的一種資料描述語言。

gRPC和Protocol Buffers的關系就好比浏覽器和HTML的關系,不互相依賴,但是需要互相配合使用,才能達到最好的效果。

2.3 Protocol Buffers基本文法

Protocol Buffers是一個帶有​

​.proto​

​擴充名的普通文本檔案。

協定緩沖區資料被構造為消息,其中每條消息都是一個小的資訊邏輯記錄,包含一系列稱為字段的鍵值對。這是一個簡單的例子:

message Person {
  string name = 1;
  int32 id = 2;
  bool has_ponycopter = 3;
}      

一旦你指定了你的資料結構,你就可以使用協定緩沖區編譯器​

​protoc​

​​從你的原型定義中以你喜歡的語言生成資料通路類。這些為每個字段提供了簡單的通路器,例如​

​name()​

​​and ​

​set_name()​

​,以及将整個結構序列化/解析到原始位元組的方法。

在普通的 proto 檔案中定義 gRPC 服務,将 RPC 方法參數和傳回類型指定為協定緩沖區消息:

// The greeter service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}      

gRPC 使用​

​protoc​

​特殊的 gRPC 插件從 proto 檔案生成代碼:将獲得生成的 gRPC 用戶端和伺服器代碼,以及用于填充、序列化和檢索消息類型的正常協定緩沖區代碼。

3 gRPC的四種服務提供方法

3.1 Unary RPC

一進制 RPC,其中用戶端向伺服器發送單個請求并獲得單個響應,就像正常的函數調用一樣。

rpc SayHello(HelloRequest) returns (HelloResponse);      

3.2 Server streaming RPC

伺服器流式 RPC,其中用戶端向伺服器發送請求并擷取流以讀回一系列消息。用戶端從傳回的流中讀取,直到沒有更多消息為止。gRPC 保證單個 RPC 調用中的消息順序。

rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);      

3.3 Client streaming RPC

用戶端流式 RPC,其中用戶端寫入一系列消息并将它們發送到伺服器,再次使用提供的流。一旦用戶端完成了消息的寫入,它就會等待伺服器讀取它們并傳回它的響應。gRPC 再次保證了單個 RPC 調用中的消息順序。

rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);      

3.4 Bidirectional streaming RPC

雙向流式 RPC,雙方使用讀寫流發送一系列消息。這兩個流獨立運作,是以用戶端和伺服器可以按照他們喜歡的任何順序讀取和寫入:例如,伺服器可以在寫入響應之前等待接收所有用戶端消息,或者它可以交替讀取消息然後寫入消息,或其他一些讀取和寫入的組合。保留每個流中消息的順序。

rpc BidiHello(stream HelloRequest) returns (stream HelloResponse)      

4 gRPC的生命周期

玩轉gRPC—深入概念與原理

4.1 服務的提供

RPC服務的提供主要包括以上四種服務提供方法。

4.2 截止日期/逾時

gRPC 允許用戶端指定在 RPC 因錯誤而終止之前,他們願意等待 RPC 完成多長時間​

​DEADLINE_EXCEEDED​

​。在伺服器端,伺服器可以查詢特定的 RPC 是否已逾時,或者還剩多少時間來完成 RPC。

指定期限或逾時是特定于語言的:一些語言 API 根據逾時工作,而一些語言 API 根據期限工作。

4.3 RPC 終止

在 gRPC 中,用戶端和伺服器都對調用是否成功做出獨立的本地判斷,并且它們的結論可能不比對。這意味着,例如,可能有一個 RPC 在伺服器端成功完成但在用戶端失敗。伺服器也可以在用戶端發送所有請求之前決定完成。

4.4 取消 RPC

用戶端或伺服器都可以随時取消 RPC。取消會立即終止 RPC,以便不再進行任何工作。

5 gRPC通信原理

衆所周知,gRPC是基于HTTP2的,而HTTP2又是一個相對HTTP1.1比較新的概念,是以在探究gRPC原理之前有必要先了解下HTTP2是怎樣的。

5.1 HTTP2

  • HTTP/2 的規範于 2015 年5 月釋出,旨在解決其前身的一些可擴充性問題,在許多方面改進了 HTTP/1.1 的設計,最重要的是提供了連接配接上的語義映射。

    建立 HTTP 連接配接的開銷很大。您必須建立 TCP 連接配接、使用 TLS 保護該連接配接、交換标頭和設定等。HTTP/1.1 通過将連接配接視為長期存在的、可重用的對象來簡化此過程。HTTP/1.1 連接配接保持空閑,以便可以通過現有的空閑連接配接發送到同一目的地的新請求。雖然連接配接重用緩解了這個問題,但一個連接配接一次隻能處理一個請求——它們是 1:1 耦合的。如果要發送一條大消息,新請求必須要麼等待它完成(導緻 隊列阻塞),要麼更頻繁地為啟動另一個連接配接付出代價。

  • HTTP/2 通過在連接配接之上提供一個語義層: 流,進而進一步擴充了持久連接配接的概念。流可以被認為是一系列語義連接配接的消息,稱為 幀。流可能是短暫的,例如請求使用者狀态的一進制流(在 HTTP/1.1 中,這可能等同于 ​

    ​GET /users/1234/status​

    ​)。随着頻率的增加,它的壽命很長。接收者可能會建立一個長期存在的流,進而實時連續接收使用者狀态消息,而不是向 /users/1234/status 端點發出單獨的請求。流的主要優點是連接配接并發,即在單個連接配接上交錯消息的能力。
  • 流量控制

    然而,并發流包含一些微妙的陷阱。考慮以下情況:同一連接配接上的兩個流 A 和 B。流 A 接收大量資料,遠遠超過它在短時間内可以處理的資料。最終,接收者的緩沖區被填滿,TCP 接收視窗限制了發送者。這對于 TCP 來說都是相當标準的行為,但這種情況對于流來說是不利的,因為兩個流都不會接收更多資料。理想情況下,流 B 應該不受流 A 的緩慢處理的影響。

    HTTP/2 通過提供流控制 機制作為流規範的一部分來解決這個問題。流控制用于限制每個流(和每個連接配接)的未完成資料量。它作為一個信用系統運作,其中接收方配置設定一定的“預算”,發送方“花費”該預算。更具體地說,接收方配置設定一些緩沖區大小(“預算”),發送方通過發送資料填充(“花費”)緩沖區。接收方使用特殊用途的WINDOW_UPDATE幀向發送方通告可用的額外緩沖區 . 當接收方停止廣播額外的緩沖區時,發送方必須在緩沖區(其“預算”)耗盡時停止發送消息。

    使用流控制,并發流可以保證獨立的緩沖區配置設定。再加上輪詢請求發送,所有大小、處理速度和持續時間的流都可以在單個連接配接上進行多路複用,而無需關心跨流問題。

  • 更智能的代理

    HTTP/2 的并發屬性允許代理具有更高的性能。例如,考慮一個接受和轉發尖峰流量的 HTTP/1.1 負載平衡器:當出現尖峰時,代理會啟動更多連接配接來處理負載或将請求排隊。前者——新連接配接——通常是首選(在某種程度上);這些新連接配接的缺點不僅在于等待系統調用和套接字的時間,還在于在 發生TCP 慢啟動時未充分利用連接配接所花費的時間。

    相比之下,考慮一個配置為每個連接配接多路複用 100 個流的 HTTP/2 代理。一些請求的峰值仍然會導緻新的連接配接被啟動,但與 HTTP/1.1 對應的連接配接數相比隻有 1/100 個連接配接。更籠統地說:如果n 個 HTTP/1.1 請求發送到一個代理,則 n 個 HTTP/1.1 請求必須出去;每個請求都是一個有意義的資料請求/有效負載,請求是 1:1 的連接配接。相反,使用 HTTP/2 發送到代理的 n請求需要n 個 流,但 不需要n 個 連接配接!

5.2 gRPC與HTTP2

gRPC 引入了三個新概念:通道、遠端過程調用 (RPC) 和消息。三者之間的關系很簡單:每個通道可能有很多 RPC,而每個 RPC 可能有很多消息。

玩轉gRPC—深入概念與原理

通道是 gRPC 中的一個關鍵概念。HTTP/2 中的流支援在單個連接配接上進行多個并發會話;**通道通過在多個并發連接配接上啟用多個流來擴充這個概念。**從表面上看,頻道為使用者發送消息提供了一個簡單的界面;然而,在引擎蓋下,大量的工程投入到保持這些連接配接的活力、健康和利用上。

通道代表到端點的虛拟連接配接,實際上可能由許多 HTTP/2 連接配接支援。RPC 與連接配接相關聯(此關聯将在後面進一步描述)。RPC 實際上是普通的 HTTP/2 流。消息與 RPC 相關聯并作為 HTTP/2 資料幀發送。更具體地說,消息是在資料幀之上*分層的。*一個資料幀可能有很多 gRPC 消息,或者如果一個 gRPC 消息非常大它可能跨越多個資料幀。

6 總結

好了,到這裡關于gRPC的講解就差不多了,歸根結底,gRPC是一個網絡協定,既然是網絡協定就難逃網絡I/O,是以也正是I/O多路複用成就了HTTP2,進而成就了gRPC,下一篇文章,讓我們深入淺出網絡I/O模型!

參考文章:

繼續閱讀