深入淺出gRPC

一、gRPC介紹

gRPC 是在 HTTP/2 之上實現的 RPC 框架,HTTP/2 是第 7 層(應用層)協議,它運行在 TCP(第 4 層 - 傳輸層)協議之上,相比于傳統的 REST/JSON 機制有諸多的優點:

  1. 基于 HTTP/2 之上的二進制協議(Protobuf 序列化機制);
  2. 一個連接上可以多路復用,并發處理多個請求和響應;
  3. 多種語言的類庫實現;
  4. 服務定義文件和自動代碼生成(.proto 文件和 Protobuf 編譯工具)。

此外,gRPC 還提供了很多擴展點,用于對框架進行功能定制和擴展,例如,通過開放負載均衡接口可以無縫的與第三方組件進行集成對接(Zookeeper、域名解析服務、SLB 服務等)。

二、gRPC服務調用原理

一個完整的 RPC 調用流程示例如下:

RPC 請求消息發送流程

gRPC 默認基于 Netty HTTP/2 + PB 進行 RPC 調用,請求消息發送流程如下所示:

image

RPC 響應接收和處理流程

gRPC 客戶端響應消息的接收入口是 NettyClientHandler,它的處理流程如下所示:

并行調用和異步調用

要解決串行調用效率低的問題,有兩個解決對策:

  1. 并行服務調用,一次 I/O 操作,可以發起批量調用,然后同步等待響應;
  2. 異步服務調用,在同一個業務線程中異步執行多個服務調用,不阻塞業務線程。

采用并行服務調用的偽代碼示例:

ParallelFuture future = ParallelService.invoke(serviceName [], methodName[], args []);
List<Object> results = future.get(timeout);// 同步阻塞式獲取批量服務調用的響應列表

并行服務調用的一種實現策略如下所示:

異步服務調用的工作原理如下:


異步服務調用相比于同步服務調用有兩個優點:

  1. 化串行為并行,提升服務調用效率,減少業務線程阻塞時間。
  2. 化同步為異步,避免業務線程阻塞。

基于 Future-Listener 的純異步服務調用代碼示例如下:

xxxService1.xxxMethod(Req);
Future f1 = RpcContext.getContext().getFuture();
Listener l = new xxxListener();
f1.addListener(l);
class xxxListener{
public void operationComplete(F future)
{ // 判斷是否執行成功,執行后續業務流程}
     }

理解誤區

1、異步服務就是異步嗎?

實際上,通信框架基于 NIO 實現,并不意味著服務框架就支持異步服務調用了,兩者本質上不是同一個層面的事情。在 RPC/ 微服務框架中,引入 NIO 帶來的好處是顯而易見的:

  • 所有的 I/O 操作都是非阻塞的,避免有限的 I/O 線程因為網絡、對方處理慢等原因被阻塞;
  • 多路復用的 Reactor 線程模型:基于 Linux 的 epoll 和 Selector,一個 I/O 線程可以并行處理成百上千條鏈路,解決了傳統同步 I/O 通信線程膨脹的問題。

NIO 只解決了通信層面的異步問題,跟服務調用的異步沒有必然關系,也就是說,即便采用傳統的 BIO 通信,依然可以實現異步服務調用,只不過通信效率和可靠性比較差而已。

對異步服務調用和通信框架的關系進行說明:


用戶發起遠程服務調用之后,經歷層層業務邏輯處理、消息編碼,最終序列化后的消息會被放入到通信框架的消息隊列中。業務線程可以選擇同步等待、也可以選擇直接返回,通過消息隊列的方式實現業務層和通信層的分離是比較成熟、典型的做法,目前主流的 RPC 框架或者 Web 服務器很少直接使用業務線程進行網絡讀寫。

通過上圖可以看出,采用 NIO 還是 BIO 對上層的業務是不可見的,雙方的匯聚點就是消息隊列,在 Java 實現中它通常就是個 Queue。業務線程將消息放入到發送隊列中,可以選擇主動等待或者立即返回,跟通信框架是否是 NIO 沒有任何關系。因此不能認為 I/O 異步就代表服務調用也是異步的。

2、異步服務調用性能肯定更高嗎?

對于 I/O 密集型,資源不是瓶頸,大部分時間都在同步等應答的場景,異步服務調用會帶來巨大的吞吐量提升,資源使用率也可以提高,更加充分的利用硬件資源提升性能。

另外,對于時延不穩定的接口,例如依賴第三方服務的響應速度、數據庫操作類等,通常異步服務調用也會帶來性能提升。

但是,如果接口調用時延本身都非常小(例如毫秒級),內存計算型,不依賴第三方服務,內部也沒有 I/O 操作,則異步服務調用并不會提升性能。能否提升性能,主要取決于業務的應用場景。

普通 RPC 調用

普通的 RPC 調用提供了三種實現方式:

  1. 同步阻塞式服務調用,通常實現類是 xxxBlockingStub(基于 proto 定義生成)。
  2. 異步非阻塞調用,基于 Future-Listener 機制,通常實現類是 xxxFutureStub。
  3. 異步非阻塞調用,基于 Reactive 的響應式編程模式,通常實現類是 xxxStub。

Streaming 模式服務調用

gRPC 服務調用支持同步和異步方式,同時也支持普通的 RPC 和 streaming 模式,可以最大程度滿足業務的需求。
對于 streaming 模式,可以充分利用 HTTP/2.0 協議的多路復用功能,實現在一條 HTTP 鏈路上并行雙向傳輸數據,有效的解決了 HTTP/1.X 的數據單向傳輸問題,在大幅減少 HTTP 連接的情況下,充分利用單條鏈路的性能,可以媲美傳統的 RPC 私有長連接協議:更少的鏈路、更高的性能:

gRPC 的網絡 I/O 通信基于 Netty 構建,服務調用底層統一使用異步方式,同步調用是在異步的基礎上做了上層封裝。因此,gRPC 的異步化是比較徹底的,對于提升 I/O 密集型業務的吞吐量和可靠性有很大的幫助。

三、gRPC線程模型

影響 RPC 框架性能的三個核心要素如下:

  1. I/O 模型:用什么樣的通道將數據發送給對方,BIO、NIO 或者 AIO,IO 模型在很大程度上決定了框架的性能;
  2. 協議:采用什么樣的通信協議,Rest+ JSON 或者基于 TCP 的私有二進制協議,協議的選擇不同,性能模型也不同,相比于公有協議,內部私有二進制協議的性能通常可以被設計的更優;
  3. 線程:數據報如何讀取?讀取之后的編解碼在哪個線程進行,編解碼后的消息如何派發,通信線程模型的不同,對性能的影響也非常大。

gRPC 線程模型

消息的序列化和反序列化均由 gRPC 線程負責,而沒有在 Netty 的 Handler 中做 CodeC,原因如下:Netty4 優化了線程模型,所有業務 Handler 都由 Netty 的 I/O 線程負責,通過串行化的方式消除鎖競爭,原理如下所示:

如果大量的 Handler 都在 Netty I/O 線程中執行,一旦某些 Handler 執行比較耗時,則可能會反向影響 I/O 操作的執行,像序列化和反序列化操作,都是 CPU 密集型操作,更適合在業務應用線程池中執行,提升并發處理能力。因此,gRPC 并沒有在 I/O 線程中做消息的序列化和反序列化。

改進點思考

1、時間可控的接口調用直接在 I/O 線程上處理

gRPC 采用的是網絡 I/O 線程和業務調用線程分離的策略,大部分場景下該策略是最優的。但是,對于那些接口邏輯非常簡單,執行時間很短,不需要與外部網元交互、訪問數據庫和磁盤,也不需要等待其它資源的,則建議接口調用直接在 Netty /O 線程中執行,不需要再投遞到后端的服務線程池。避免線程上下文切換,同時也消除了線程并發問題。

例如提供配置項或者接口,系統默認將消息投遞到后端服務調度線程,但是也支持短路策略,直接在 Netty 的 NioEventLoop 中執行消息的序列化和反序列化、以及服務接口調用。

2、減少鎖競爭

當前 gRPC 的線程切換策略如下:

優化之后的 gRPC 線程切換策略:

通過線程綁定技術(例如采用一致性 hash 做映射), 將 Netty 的 I/O 線程與后端的服務調度線程做綁定,1 個 I/O 線程綁定一個或者多個服務調用線程,降低鎖競爭,提升性能。

四、gRPC 安全性設計

RPC 調用安全主要涉及如下三點:

  1. 個人 / 企業敏感數據加密:例如針對個人的賬號、密碼、手機號等敏感信息進行加密傳輸,打印接口日志時需要做數據模糊化處理等,不能明文打印;
  2. 對調用方的身份認證:調用來源是否合法,是否有訪問某個資源的權限,防止越權訪問;
  3. 數據防篡改和完整性:通過對請求參數、消息頭和消息體做簽名,防止請求消息在傳輸過程中被非法篡改。

敏感數據加密傳輸

1. 基于 SSL/TLS 的通道加密
2. 針對敏感數據的單獨加密

有些 RPC 調用并不涉及敏感數據的傳輸,或者敏感字段占比較低,為了最大程度的提升吞吐量,降低調用時延,通常會采用 HTTP/TCP + 敏感字段單獨加密的方式,既保障了敏感信息的傳輸安全,同時也降低了采用 SSL/TLS 加密通道帶來的性能損耗,對于 JDK 原生的 SSL 類庫,這種性能提升尤其明顯。

它的工作原理如下所示:

image

通常使用 Handler 攔截機制,對請求和響應消息進行統一攔截,根據注解或者加解密標識對敏感字段進行加解密,這樣可以避免侵入業務。

認證和鑒權

1. 身份認證
2. 權限管控

在 RPC 調用領域比較流行的是基于 OAuth2.0 的權限認證機制,它的工作原理如下:

數據完整性和一致性

利用消息摘要可以保障數據的完整性和一致性,它的特點如下:

  • 單向 Hash 算法,從明文到密文的不可逆過程,即只能加密而不能解密;
  • 無論消息大小,經過消息摘要算法加密之后得到的密文長度都是固定的;
  • 輸入相同,則輸出一定相同。

目前常用的消息摘要算法是 SHA-1、MD5 和 MAC,MD5 可產生一個 128 位的散列值。 SHA-1 則是以 MD5 為原型設計的安全散列算法,可產生一個 160 位的散列值,安全性更高一些。MAC 除了能夠保證消息的完整性,還能夠保證來源的真實性。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容