用通俗的語言介紹 RPC 框架的架構原理
本文轉載自微信公眾號「愛笑的架構師」,作者雷小帥。轉載本文請聯系愛笑的架構師公眾號。
2022 年認真干點事!
動手實現一個簡易的 RPC 輪子真的很難嗎?no no no,很簡單的,不信你把文章看完(doge)。
動動手
RPC 框架典型的架構
典型的 RPC 架構大致可以分為三個部分:
(1)服務提供者(RPC Server):運行在服務器端,提供服務接口定義與服務實現類。
(2)注冊中心(Registry):運行在服務器端,負責將本地服務發布成遠程服務,管理遠程服務,提供給服務消費者使用。
(3)服務消費者(RPC Client):運行在客戶端,通過遠程代理對象調用遠程服務。
通過上面的圖可以看出,一次簡單的 RPC 調用可以分為以下幾個步驟:
(1)服務提供者啟動后主動向服務注冊中心注冊機器ip、端口以及提供的服務列表;
(2)服務消費者啟動時向服務注冊中心獲取服務提供方地址列表,在本地緩存一份;
(3)服務消費者通過本地調用的方式調用服務,調用模塊收到請求后通過負載均衡策略選取合適的遠程服務地址;
(4)協議模塊負責將方法、入參等信息序列化(編碼)成能夠進行網絡傳輸的消息體,并將消息通過網絡發送給服務端;
(5)服務端收到消息后進行解碼(反序列化操作)。
(6)根據解碼結果調用本地的服務進行相關處理;
(7)服務端將處理返回的結果進行序列化(編碼),并將結果通過網絡發送至服務消費者;
(8)服務消費者收到消息后進行解碼最終得到結果;
敲黑板:在不同的 RPC 框架實現中步驟 1、2、3的順序可能有些不同。
RPC 核心功能
一個完整的商用 RPC 框架有很多功能,最最核心的基本就是三個:服務尋址、數據編解碼、網絡傳輸。
服務尋址
如果是本地調用,被調用的方法在同一個進程內,操作系統或虛擬機可以地址空間找到;但是在遠程調用中,這是行不通的,因為兩個進程的地址空間是完全不一樣的,并且也無法知道遠端的進程在何處。
要想實現遠程調用,我們需要對服務消費者和服務提供者進行約束:
- 在遠程過程調用中所有的函數都必須有一個ID,這個 ID 在整套系統中是唯一確定的。
- 服務消費者在做遠程過程調用時,發送的消息體中必須攜帶這個 ID。
- 服務消費者和服務提供者分別維護一個函數和 ID 的對應表。
當服務消費者需要進行遠程調用時,它就查一下這個表,找出對應的 ID,然后把它傳給服務端,服務端也通過查表,來確定客戶端需要調用的函數,然后執行相應函數的代碼。
上面說的可能比較抽象,通俗一點就是服務消費者如何尋找服務提供者,這就是服務尋址。
服務尋址的實現方式有很多種,比較常見的是:服務注冊中心。要調用服務,首先你需要一個服務注冊中心去查詢對方服務都有哪些實例,然后根據負載均衡策略擇優選一。
像 Dubbo 框架的服務注冊中心是可以配置的,官方推薦使用 Zookeeper。
數據編解碼(序列化和反序列化)
對計算機網絡稍微有一點了解的同學都知道,數據在網絡中傳輸是二進制的:01010101010101010,類似這種,只有二進制數據才能在網絡中傳輸。
那一個客戶端調用遠程服務的一個方法,像方法入參這些必然需要轉換成二進制才能進行傳輸,這種將對象轉換成二進制流的過程就叫做序列化編碼。
服務端接收到二進制流不能識別,勢必要將二進制流轉換成對象,這個逆過程就叫做反序列化解碼。
一般場景下是可以將序列化編碼簡稱為序列化。
敲黑板:
如果非要較真,嚴格來說序列化和編碼是兩個不同的概念,我畫一張圖大家都明白了。
序列化和編碼的對比
序列化+編碼的逆過程就是:解碼+反序列化。
網絡傳輸
提起網絡傳輸大家腦海里肯定馬上就能想到 TCP/IP四層模型、OSI 七層模型,那通常 RPC 會選擇那一層作為傳輸協議呢?
在回答這個問題前我們先看下 RPC 需要網絡傳輸實現什么功能。
客戶端的數據經過序列化+編碼后,就需要通過網絡傳輸到服務端。網絡傳輸層需要把前面說的函數 ID 和序列化后的參數字節流傳給服務端,服務端處理完然后再把序列化后的調用結果傳回客戶端。
原則上只要能實現上面這個功能的都可以作為傳輸層來使用,具體協議沒有限制。
我們先來看下 TCP 協議,TCP 連接可以是按需連接,需要調用的時候就先建立連接,調用結束后就立馬斷掉,也可以是長連接,客戶端和服務器建立起連接之后保持長期持有,不管此時有無數據包的發送,可以配合心跳檢測機制定期檢測建立的連接是否存活有效。
由此可見 TCP 的性能確實很好,因此市面上大部分 RPC 框架都使用 TCP 協議,但也有少部分框架使用其他協議,比如 gRPC 就基于 HTTP2 來實現的。
敲黑板:
數據編解碼和網絡傳輸可以有多種組合方式,比如常見的有:HTTP+JSON, Dubbo 協議+TCP 等。
常見的 RPC 框架
說了這么多 RPC 相關的技術,我們盤點一下市面上常用的 RPC 框架。
- RMI(Sun/Oracle)
- Thrift(Facebook/Apache)
- gRPC(Google)
- Finagle(Twitter)
- Dubbo(阿里巴巴/Apache)
- Motan(新浪微博)
- brpc(百度/Apache)
- ……歡迎大家補充其他的。
總結
(1)服務提供者需要以某種形式提供服務調用相關的信息,包括但不限于服務接口定義、數據結構、或者中間態的服務定義文件。例如Facebook的 Thrift 框架的IDL文件,Web service的 WSDL 文件;服務的消費者需要通過一定的場景獲取遠程服務調用相關的信息。
(2)遠程代理對象:服務消費者用的服務實際是遠程服務的本地代理,說白了就是通過動態代理來實現的。
(3)序列化:畢竟是遠程通信,需要將對象轉化成二進制流進行傳輸。不同的RPC框架應用的場景不同,在序列化上也會采取不同的技術。
(4)通信:RPC框架與具體的協議無關。Netty 是一個高性能的網絡通信框架。
因此要實現一個 RPC 框架,只需要把上面四點實現了就基本完成了。大家學會了嗎?