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