RPC框架GPRC淺析


RPC框架中數據的傳輸通常有兩種:二進制傳輸,和文本類傳輸 。
二進制傳輸的優點是:傳輸性能好,因為要寫協議文件,所以更嚴謹。
缺點是:二進制難以跨語言,

文本類傳輸的優點是:可以跨語言,而且由於不用寫協議文件,使用更靈活 。
缺點是:傳輸性能稍微要差一些。

GRPC的特點是不僅采用了二進制傳輸,保證了傳輸性能,還滿足跨語言,保證了靈活性。

序列化

GPRC的二進制序列化協議是Protocol Buffers。
Protocol Bufers 是一款壓縮效率極高的序列化協議,有很多設計精巧的序列化方法。
對於 int 類型 32 位的,一般都需要 4 個 Byte 進行存儲。在 Protocol Bufers 中,使用的是變長整數的形式。對於每一個 Byte 的 8 位,最高位都有特殊的含義。

如果該位為 1 ,表示這個數字沒完,后續的 Byte 也屬於這個數字;如果該位為 0 ,則這個數字到此結束。其他的 7 個 Bit才是用來表示數字的內容。因此,小於 128 的數字都可以用一個 Byte 表示;大於 128 的數字,比如 130 ,會用兩個字節來表示。

對於每一個字段,使用的是 TLV ( Tag , Length , Value )的存儲辦法。
其中 Tag = (feld_num << 3) | wire_type 。 feld_num 就是在 proto 文件中,給每個字段指定唯一的數字標識,而 wire_type 用於標識后面的數據類型。

網絡傳輸

如果是 Java 技術棧, GRPC 的客戶端和服務器之間通過 Netty Channel 作為數據通道,每個請求都被封裝成 HTTP 2.0 的 Stream 。Netty是一個高效的基於異步IO的網絡傳輸框架

HTTP 2.0協議將一個TCP的連接,切分成多個流,每個流都
有自己的 ID ,而且流是有優先級的。流可以是客戶端發往服務端,也可以是服務端發往客戶端。它其實只是一個虛擬的通道。
HTTP 2.0 還將所有的傳輸信息分割為更小的消息和幀,並對它們采用二進制格式編碼。
通過這兩種機制, HTTP 2.0 的客戶端可以將多個請求分到不同的流中,然后將請求內容拆成幀,進行二進制傳輸。這些幀可以打散亂序發送, 然后根據每個幀首部的流標識符重新
組裝,並且可以根據優先級,決定優先處理哪個流的數據。

由於基於 HTTP 2.0 , GRPC 和其他的 RPC 不同,可以定義四種服務方法。
第一種,也是最常用的方式是 單向RPC ,即客戶端發送一個請求給服務端,從服務端獲取一個應答,就像一次普通的函數調用。

rpc SayHello(HelloReques) returns (HelloResponse){}

第二種方式是 服務端流式RPC ,即服務端返回的不是一個結果,而是一批。客戶端發送一個請求給服務端,可獲取一個數據流用來讀取一系列消息。客戶端從返回的數據流里一直讀
取,直到沒有更多消息為止。

rpc LotsOfReplies(HelloReques) returns (sream HelloResponse){}

第三種方式為 客戶端流式RPC ,也即客戶端的請求不是一個,而是一批。客戶端用提供的一個數據流寫入並發送一系列消息給服務端。一旦客戶端完成消息寫入,就等待服務端讀取這
些消息並返回應答。

rpc LotsOfGreetings(sream HelloReques) returns (HelloResponse) {}

第四種方式為 雙向流式 RPC ,即兩邊都可以分別通過一個讀寫數據流來發送一系列消息。這兩個數據流操作是相互獨立的,所以客戶端和服務端能按其希望的任意順序讀寫,服務端
可以在寫應答前等待所有的客戶端消息,或者它可以先讀一個消息再寫一個消息,或者讀寫相結合的其他方式。每個數據流里消息的順序會被保持。

rpc BidiHello(sream HelloReques) returns (sream HelloResponse){}

如果基於 HTTP 2.0 ,客戶端和服務器之間的交互方式要豐富得多,不僅可以單方向遠程調用,還可以實現當服務端狀態改變的時候,主動通知客戶端。
至此,傳輸問題得到了解決。

服務治理

GRPC 本身沒有提供服務發現的機制,需要借助其他的組件,發現要訪問的服務端,在多個服務端之間進行容錯和負載均衡。
其實負載均衡本身比較簡單, LVS 、 HAProxy 、 Nginx 都可以做,關鍵問題是如何發現服務端,並根據服務端的變化,動態修改負載均衡器的配置。

有一種對於 GRPC 支持比較好的負載均衡器 Envoy 。其實 Envoy 不僅僅是負載均衡器,它還是一個高性能的 C++ 寫的 Proxy 轉發器,可以配置非常靈活的轉發規則。
這些規則可以是靜態的,放在配置文件中的,在啟動的時候加載。要想重新加載,一般需要重新啟動,但是 Envoy 支持熱加載和熱重啟,這在一定程度上緩解了這個問題。
當然,最好的方式是將規則設置為動態的,放在統一的地方維護。這個統一的地方在 Envoy 眼中被稱為服務發現( Discovery Service ),過一段時間去這里拿一下配置,就修改了
轉發策略。
無論是靜態的,還是動態的,在配置里面往往會配置四個東西。
第一個是 listener 。 Envoy 既然是 Proxy ,專門做轉發,就得監聽一個端口,接入請求,然后才能夠根據策略轉發,這個監聽的端口就稱為 listener 。
第二個是 endpoint ,是目標的 IP 地址和端口。這個是 Proxy 最終將請求轉發到的地方。
第三個是 cluster 。一個 cluster 是具有完全相同行為的多個 endpoint ,也即如果有三個服務端在運行,就會有三個 IP 和端口,但是部署的是完全相同的三個服務,它們組成一
個 cluster ,從 cluster 到 endpoint 的過程稱為負載均衡,可以輪詢。
第四個是 route 。有時候多個 cluster 具有類似的功能,但是是不同的版本號,可以通過 route 規則,選擇將請求路由到某一個版本號,也即某一個 cluster 。
如果是靜態的,則將后端的服務端的 IP 地址拿到,然后放在配置文件里面就可以了。

參考資料

極客時間的《趣談網絡協議》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM