gRPC 作為一個高性能的遠程過程調用(RPC)框架,廣泛應用於多個領域和產品中。舉幾個比較出名的例子:
Kubernetes:Kubernetes 的 API 服務器和其他組件(如 kubelet)之間的通信使用 gRPC 來實現高效的數據傳輸。Netflix:作為流媒體服務的領導者,Netflix 需要處理大量的網絡請求和數據傳輸。Netflix 使用 gRPC 來優化服務之間的通信效率,特別是在高負載的微服務架構中。GitHub:GitHub 在其內部系統中使用 gRPC 來提高服務調用的效率。
gRPC 在雲原生生態中最佳實踐除了 Kubernetes,應該就是 Etcd 了吧。不知道有多少人是和我一樣,是先了解 Etcd,才去瞭解 gRPC 的。
etcd 使用 gRPC 作為其主要的通信協議。這意味着 etcd 的客户端與服務器之間的通信是通過 gRPC 實現的。
不僅得益於 gRPC 提供的高效二進制傳輸,因為 gRPC 是跨語言的,使得 etcd 也可以在不同的編程環境中輕鬆使用。
本文會講解一下 gRPC 在 etcd 中的應用場景。
1. 應用
etcd 是一個分佈式鍵值存儲系統,廣泛用於分佈式系統中的配置管理、服務發現和協調。etcd 是由 CoreOS 開發的,採用了 Raft 共識算法來保證數據的強一致性。gRPC 在 etcd 中扮演了關鍵角色,提供了高效的服務間通信機制。以下是 gRPC 在 etcd 中的具體應用和實現細節:
1.1. 通信框架
-
高性能通信:
- gRPC 提供了高性能的通信能力,支持 HTTP/2 協議,這對於 etcd 的高吞吐量和低延遲需求非常重要。
- HTTP/2 的多路複用特性允許 etcd 在同一 TCP 連接上同時處理多個請求,減少了連接開銷。
-
多語言支持:
- gRPC 支持多種編程語言,允許 etcd 的客户端在不同的語言環境中進行無縫通信。
- 這使得 etcd 可以被廣泛應用於多語言開發的分佈式系統中。
1.2. API 定義
-
Protocol Buffers:
- etcd 使用 Protocol Buffers(protobuf)來定義其 API 接口。這種方式提供了高效的序列化和反序列化能力。
- protobuf 文件定義了 etcd 的所有服務接口和消息格式,這些定義被用來生成 gRPC 服務代碼。
1.3. 服務實現
-
客户端與服務器交互:
- etcd 使用 gRPC 實現了客户端與服務器之間的交互。客户端通過 gRPC 調用 etcd 服務器的各種 API,例如讀取和寫入鍵值對、監聽鍵的變化等。
- gRPC 的流特性被用於實現 etcd 的監控功能(watch),客户端可以通過流來監聽鍵值的變化。
-
負載均衡與服務發現:
- etcd 可以與其他服務發現和負載均衡機制集成,通過 gRPC 提供的元數據機制實現動態的服務發現和負載均衡。
1.4. 安全性
-
傳輸層安全:
- etcd 支持通過 gRPC 的傳輸層安全性(TLS)來加密客户端與服務器之間的通信,確保數據傳輸的安全性。
- 通過配置 TLS,etcd 可以防止中間人攻擊和數據竊聽。
1.5. 高可用性與容錯
-
Raft 共識算法:
- etcd 通過 Raft 共識算法實現了數據的高可用性和一致性。gRPC 在此過程中負責節點之間的通信。
- etcd 集羣中的節點通過 gRPC 相互通信,進行日誌複製和領導者選舉等操作,確保集羣的一致性和可靠性。
1.6. 擴展性
-
動態擴展:
- 使用 gRPC,etcd 可以方便地擴展其 API 接口,添加新的功能和服務,而無需大規模重構。
- 客户端與服務器的接口定義可以通過更新 protobuf 文件和重新生成代碼來實現動態擴展。
2. 四種模式對應場景
etcd 在其實現中使用了 gRPC 的多種通信模式,包括單一響應、客户端流、服務器流和雙向流。每種模式用於不同的場景和功能。以下是 etcd 中使用這些模式的詳細説明:
1.1. 單一響應(Unary RPC)
單一響應模式是 gRPC 中最基本的模式,客户端發送一個請求到服務器,然後等待一個響應。這種模式在 etcd 中用於大多數簡單的鍵值操作,比如讀取和寫入單個鍵值對。
示例操作:
Put:將一個鍵值對寫入 etcd。Get:從 etcd 中讀取一個鍵值對。
1.2. 客户端流(Client Streaming RPC)
客户端流模式允許客户端發送一個請求流到服務器,服務器在接收完所有請求後返回一個響應。在 etcd 中,這種模式並不常用,因為大多數操作是以單個請求為基礎的。
潛在應用:
- 批量寫入:客户端可以發送多個鍵值對作為一個流進行批量寫入。
1.3. 服務端流(Server Streaming RPC)
服務端流模式允許服務器返回一個響應流給客户端,客户端在接收到所有響應之前可以一直讀取。etcd 使用這種模式來實現對鍵值變化的監視。
示例操作:
Watch:客户端可以訂閲特定鍵或前綴的變化,etcd 服務器會以流的形式發送所有變化通知。
1.4. 雙向流(Bidirectional Streaming RPC)
雙向流模式允許客户端和服務器同時發送和接收消息流。這種模式為複雜的交互提供了靈活性。在 etcd 中,雙向流用於實現高級功能,如複雜的協作和實時同步。
示例應用:
- 複雜的實時同步:節點間的實時數據同步和一致性維護。
例如 etcd 的內部節點通信(如通過 Raft 協議實現的節點間同步和選舉)也可能利用 gRPC 的流式通信特性,以保證分佈式一致性和高可用性。
3. Watch 機制
前面説 etcd 中的 Watch 機制是基於 gRPC 服務端流實現的,那這裏詳細看看實現的過程。
- 持續性連接:當客户端向 etcd 服務器發起
Watch請求時,服務器會啓動一個持續的流向客户端發送數據。這意味着只要客户端保持連接且不取消訂閲,服務端的流會一直保持打開狀態,服務器就會持續地將相關的鍵值變化發送給客户端。 - 實時更新:
Watch的主要目的是讓客户端能夠實時獲取鍵或前綴的變化。因此,服務器在監測到相關鍵值的變化時,會立即通過流將這些變化發送給客户端。 - 取消訂閲:客户端可以隨時選擇取消
Watch操作。當客户端發送取消請求或關閉連接時,服務器會終止流。 - 錯誤處理和重試:在實際應用中,網絡中斷或其他異常情況可能導致連接中斷。在這種情況下,客户端通常會實現自動重試邏輯,重新建立
Watch連接,以確保能夠持續接收更新。
4. 客户端併發性能
最常見的應用場景就是通過 PUT/GET 命令往 etcd 中 寫入/讀取 配置了。這部分使用的是 gRPC 的單一響應模式,每次命令執行都是一個獨立的 HTTP/2 請求,會不會有併發問題呢?
在 gRPC 中,雖然每個請求(包括單一響應模式的請求)看似獨立,但 gRPC 底層實際上是通過持久的 HTTP/2 連接來管理這些請求的。
HTTP/2 支持多路複用,這意味着可以在一個單一的 TCP 連接上同時處理多個請求和響應流。因此,即使是單一響應模式的請求,也不需要為每個請求都創建一個新的 TCP 連接。
如何處理併發寫入
- 持久連接:gRPC 客户端與服務器之間通常會保持一個持久的 TCP 連接。這個連接是在初始請求時建立的,之後的所有請求都會複用這個連接,除非連接因為某些原因關閉或失效。
- 多路複用:HTTP/2 的多路複用特性允許多個請求和響應在同一個連接上並行進行。這意味着你可以同時發送和接收多個 gRPC 請求和響應,而不必擔心 TCP 連接的瓶頸。
- 連接池:在高併發場景下,gRPC 客户端通常會維護一個連接池。連接池可以包含多個持久連接,這樣可以在需要時快速複用現有連接,而不必為每個請求重新建立連接。
- 異步和併發請求:gRPC 客户端庫通常提供異步接口,使得客户端可以在等待響應的同時發送其他請求,從而提高併發性能。
單一響應模型和流模型性能差異
既然無論單一響應模式,還是 客户端流、服務端流、雙向流,都可以在多次請求中複用同一個 tcp 連接。那麼假設有n個消息要請求響應,下面兩種方式對比:
- 單一響應模式:是發起n次HTTP/2請求響應。
- 流模式:只發起1次HTTP/2連接,但分多次流請求、響應。
因為2者都可以複用同一個tcp連接,所以都無需重新創建/銷燬連接。如果性能有差距,我們能感受是流模式會更高性能,原因可以分幾點:
- 減少請求頭開銷:在流模式下,可以在一個請求中批量發送或接收多條消息,這減少了為每條消息單獨發起請求的開銷。這種批量處理減少了協議頭的重複傳輸,提高了網絡利用率。
- 減少請求和響應的往返次數:單一響應模式中,每個請求都需要等待服務器處理並返回響應,這意味着每個請求都涉及一個完整的請求-響應往返週期。流模式允許在一個流中連續發送和接收多個消息,而無需等待每個消息的響應,這減少了多次請求-響應之間的等待時間。
- 減少連接管理:雖然可以複用同一個 tcp 連接,但如果分成多次 HTTP/2 連接,會增加連接管理和狀態維護。畢竟每次都需要判斷 tcp 連接是否存在,響應結束後還要釋放連接,連接管理有開銷。