高併發服務優化篇:詳解 RPC 的一次調用過程

只要涉及到分佈式服務,就繞不開 RPC 調用。RPC 是什麼,我認爲大部分同學都能說出個一二三。

那麼 RPC 一次調用,到底經歷了哪些過程?

一直在說 RPC 耗時優化,那到底時間耗在了哪裏? 

本篇帶大家一起來梳理清晰。再遇到面試官問 RPC,直接滅丫

Part1 前言 扯一扯 RPC 的蛋

RPC ?(Remote Procedure Call) 遠程過程調用,目的是讓調用遠程服務的體驗,就像調用本地方法一樣簡單。

已經有了 HTTP,爲啥還要實現個 RPC? 首先,兩者不在一個水平面,不好比較。http 是一種傳輸協議,RPC 由 TCP 傳輸協議和其他部分組成,算是一種架構;再者,效率和性能有所差異,Http 相比 tcp 傳輸更耗性能;再再者,定位不同,Rpc 一般用於實現內部網絡各服務間的高性能調用,Http 一般用於跨環境的數據傳輸和接口調用。

常見 RPC 有哪些? 

出鏡最多的要數 dubbo,因爲總被面試官問到;

性能優良的 grpc,google 出品,可以在任何環境下運行;

美團的 OCTO 和 pigeon,一個章魚水裏遊,一個鴿子天上飛;

京東之前的 saf,是對 dubbo 的定製化開發,後面升級到了自研的 jsf 框架,其作者之一的章老闆之前就已經是螞蟻的 P8 大佬了;

螞蟻自研的 sofaRPC 也有章老闆的參與,由於設計初衷和螞蟻內部的使用規模,功能豐富度和服務穩定性上,那是相當不錯,目前也已經開源。

Part2 一次 RPC 調用的心路歷程

一次 RPC 調用的心路歷程

如上圖所示,一次 rpc 調用的過程,基本都囊括在內:

Stub 存根

處於真正調用之前。進行場景判斷、條件過濾等,以 dubbo 爲例,可以用於壓測場景的數據 mock 等功能支持。

路由尋址和負載均衡

上面的圖其實有點不太準確,路由尋址和服務節點的負載均衡應該是一起完成的,在選定 provider 之後就是直連了。圖裏只是爲了對稱一些好看。

尋址: 以 safa 爲例,支持直連和註冊中心尋址。實現方案是在地址維護器中按配置加載直連分組和集羣分組,在客戶端指定路由策略時,進行分別獲取。

負載均衡: safa 在負載算法上要支持的相對更全面一些:一致性 hash、本機優先、隨機負載、輪詢負載、加權一致性 hash、加權輪詢。

dubbo 支持的負載均衡

sofa 支持的負載均衡

序列化和反序列化

序列化方式有很多種,包括 jdk 原生,kryo、hessian、protoStuff,thrift,JSON 等。

這裏挑兩個經常使用,但是經常遇坑的來說下:

hessian: 相比於 Java 原生序列化,效率更高、數據更小,但是需要注意,hessian 反序列化時,是將屬性都取出來放到 map 裏,因此,如果父類和子類有 name 相同的屬性,子類的會被覆蓋,因此,使用 hessian 時,要注意父子類不能有相同的屬性名。

protoStuff:  相比 Protobuf,stuff 不需要寫. proto 文件,效率上甚至比 Protobuf 更快。而快的原因之一,就是因爲其序列化方式是按對象屬性的順序來執行的,所以,如果順序變了,就會反序列化失敗。因此,在對使用了 protoStuff 序列化方式的對象新增字段時,最好是加到最後。

編碼 解碼

序列化之後爲啥還要進行編碼呢?

序列化其實是爲了將待傳輸的對象轉化成標準二進制信息,爲傳遞做準備,同時儘可能壓縮大小,方便傳輸。

而編碼,是爲了通信高效,一般的,都會加上超時策略、請求 ID、網絡協議等信息。

網絡傳輸

一般大部分的 RPC 都選 netty 作爲通信框架,而在底層是 TCP 的傳輸協議,而在上層,還有一層通信協議:

通信協議的目的,是爲了讓中間件開發者能將更多的精力放在產品功能特性實現上,而不是重複地一遍遍製造通信框架的輪子。

Part3RPC 執行耗時都耗在了哪裏

我是動圖,請多給我點時間

從上圖分析中可以看出一次 rpc 調用的具體耗時節點。

對於客戶端來說,耗時主要由:_建連時間 _+ _序列化時間 _+ 等待服務端處理時間 組成;

對於服務端來說,耗時主要由:_線程池等待時間 + __服務處理時間 + _結果序列化時間 組成。

所以,對於我們一線開發,如果要對 RPC 耗時進行調優,最需要關注的,有客戶端的路由尋址、序列化方式,有服務端的服務線程池等待、反序列化、服務端處理速率、結果序列化 這幾塊。

‘建連’,一般因爲我們採用長連接心跳檢測,是可以保證這個時間相對穩定。

比如,借鑑 sofa,用增量更新的直接分組,來加速路由尋址;採用速度更快的序列化策略;調整服務端線程池到合適的大小,即能滿足請求處理,又不至於增加過多的線程切換損耗;用異步調用的方式替代同步阻塞等等。

Part4 總結

本文從 RPC 的一次調用觸發,結合一些開源的框架代碼,給大家梳理了 RPC 的調用過程和耗時分析。讓大家對 RPC 調用有一個更直觀的體會。特別是耗時分析這一部分,對我們一線研發的開發有些直接的指導意義。

希望大家能有所得,有任何問題,歡迎留言指正、探討~

題外話:不知道大家有沒有發現,我們平常接觸的框架、系統,好大一部分都來自阿里,爲什麼呢?一方面是因爲技術確實不錯,畢竟龐大的用戶羣和複雜的業務場景對任何系統、任何技術人都是非常好的磨刀石;不過我覺得,其他大廠的框架應該也不會差多少。只不過阿里有一羣專門的人出來到處演講、吹牛逼,我們親切的稱其爲 "佈道者"。。。O(∩_∩)O~  純屬瞎扯,娛樂一下~

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/S6vwJBsiMI7aTsGgfLitTQ