溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

手寫一個RPC框架的方法教程

發(fā)布時間:2021-10-25 16:11:51 來源:億速云 閱讀:145 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“手寫一個RPC框架的方法教程”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成! 

介紹

當開發(fā)一個單體項目的時候,大家肯定都寫過類似的代碼。即服務(wù)提供方和服務(wù)調(diào)用方在一個服務(wù)中

public interface HelloService {     public String sayHello(String content); }
public class HelloServiceImpl implements HelloService {      @Override     public String sayHello(String content) {         return "hello, " + content;     } }
public class Test {      public static void main(String[] args) {         HelloService helloService = new HelloServiceImpl();         String msg = helloService.sayHello("world");         // hello world         System.out.println(msg);     } }

但是由于單體服務(wù)的諸多弊端,現(xiàn)在很多公司已經(jīng)將不相關(guān)的功能拆分到不同的服務(wù)中。

如何像調(diào)用本地服務(wù)一樣調(diào)用遠程服務(wù)呢?這時就不得不提RPC框架了(Remote Procedure  Call,遠程過程調(diào)用)。他幫我們屏蔽了網(wǎng)絡(luò)通信,序列化等操作的實現(xiàn),真正做到了調(diào)用遠程服務(wù)和調(diào)用本地服務(wù)一樣方便。

知名的RPC框架有Spring Cloud,阿里巴巴的Dubbo,F(xiàn)acebook的Thrift,Google grpc等

RPC的調(diào)用過程

手寫一個RPC框架的方法教程

一個RPC調(diào)用的過程如下

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 調(diào)用方發(fā)送請求后由代理類將調(diào)用的方法,參數(shù)組裝成能進行網(wǎng)絡(luò)傳輸?shù)南Ⅲw

  3. 調(diào)用方將消息體發(fā)送到提供方

  4. 提供方將消息進行解碼,得到調(diào)用的參數(shù)

  5. 提供方反射執(zhí)行相應(yīng)的方法,并將結(jié)果返回

下面我們就分析一下rpc框架是怎么實現(xiàn)的?有哪些地方可以擴展。為了讓大家有一個更形象的認識,我寫了一個github項目,由簡到難實現(xiàn)了一個rpc框架,歡迎star

https://github.com/erlieStar/simple-rpc

生成代理類

前面我們說過,調(diào)用方執(zhí)行方法后,實際上執(zhí)行的是代理類的方法,代理類幫我們進行序列化和編解碼操作。那么如何生成代理類呢?

我們看一下主流的做法。

Facebook的Thrift和Google的grpc都是定義一個schema文件,然后執(zhí)行程序,幫你生成客戶端代理類,以及接口。調(diào)用方直接用生成的代理類來請求,提供方繼承生成的接口即可。

這種方式最大的優(yōu)點就是能進行多語言通信,即一份schema文件可以生成Java程序,也可以生成Python程序。調(diào)用方是Java程序,提供方是Python程序都能正常進行通訊。而且是二進制協(xié)議,通訊效率比較高。

在Java中生成代理類的方式有如下幾種

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. JDK動態(tài)代理(實現(xiàn)InvocationHandler接口)

  3. 字節(jié)碼操作類庫(如cglib,Javassist)

在Dubbo中提供了2種生成代理類的方式,jdk動態(tài)代理和Javassist,默認是javassist,至于原因嗎?當然是javassist的效率更高

協(xié)議

為什么需要協(xié)議這個東西呢?Spring Cloud是通過Http協(xié)議來進行通訊的,那么Dubbo是通過哪種協(xié)議來進行通訊的?

為什么需要協(xié)議這個東西?

因為數(shù)據(jù)是以二進制的形式在網(wǎng)絡(luò)中傳輸中,RPC的請求數(shù)據(jù)并不是以一個整體發(fā)送到提供方的,而是可能被拆分成多個數(shù)據(jù)包發(fā)送出去,那提供方怎么識別數(shù)據(jù)呢?

例如一個文本ABCDEF,提供方有可能依次收到的數(shù)據(jù)為ABC DEF,也有可能為AB CD EF。提供方該怎么處理這些數(shù)據(jù)呢?

簡單啊,定個規(guī)則就可以了。這個規(guī)則可以有很多種,這里舉3個例子

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 定長協(xié)議,協(xié)議內(nèi)容長度固定,如讀取到50個byte就開始decode操作,可以參考Netty的FixedLengthFrameDecoder

  3. 特殊結(jié)束符,定義一個消息結(jié)束的分隔符,如讀到\n,表示一個數(shù)據(jù)讀取完畢了,沒有讀到就一直讀,可以參考Netty的DelimiterBasedFrameDecoder

  4. 變長協(xié)議(協(xié)議頭+協(xié)議體),用一個定長來表示消息體的長度,剩下的內(nèi)容為消息體,如果你愿意的話,協(xié)議頭還會放一些常用的屬性,Http協(xié)議的Header就是協(xié)議頭,如content-type,content-length等。可以參考Netty的DelimiterBasedFrameDecoder

Dubbo通過自定義協(xié)議來進行通訊,協(xié)議頭格式如下

手寫一個RPC框架的方法教程

每個位代表的含義如下

手寫一個RPC框架的方法教程

Dubbo為什么要自定義協(xié)議,而不用現(xiàn)成的Http協(xié)議?

最主要的原因就是自定義協(xié)議可以提高性能

Http協(xié)議的請求包比較大,有很多無用的內(nèi)容。自定義協(xié)議可以精簡很多內(nèi)容

Http協(xié)議是無狀態(tài)的,每次都要重新建立連接,響應(yīng)完畢后將連接關(guān)閉

序列化

協(xié)議頭的內(nèi)容是通過位來表示的,協(xié)議體在應(yīng)用程序中則會被封裝成對象,如Dubbo將請求封裝成Request,將響應(yīng)封裝成Response

手寫一個RPC框架的方法教程

前面我們說過網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)必須是二進制數(shù)據(jù),但調(diào)用方的入?yún)⒑吞峁┓降姆祷刂刀际菍ο螅虼诵枰蛄谢头葱蛄谢倪^程

序列化的方式有如下幾種

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. JDK原生序列化

  3. JSON

  4. Protobuf

  5. Kryo

  6. Hessian2

  7. MessagePack

我們選擇序列化的方式時,主要考慮如下幾個因素

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 效率

  3. 空間開銷

  4. 通用性和兼容性

  5. 安全性

通訊

常見的IO模型有如下四種

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 同步阻塞IO(Blocking IO)

  3. 同步非阻塞IO(Non-blocking IO)

  4. IO多路復用(IO Multiplexing)

  5. 異步IO(Asynchronous IO)

因為RPC一般用在高并發(fā)的場景下,因此我們選擇IO多路復用這種模型,Netty的IO多路復用基于Reactor開發(fā)模式來實現(xiàn),后續(xù)的文章我會分析一下這種開發(fā)模式是如何支持高并發(fā)的

注冊中心

注冊中心的作用和電話簿類似。保存了服務(wù)名稱和具體的服務(wù)地址之間的映射關(guān)系,當我們想和某個服務(wù)進行通信時,只需要根據(jù)服務(wù)名就能查到服務(wù)的地址。

更重要的是這個電話簿是動態(tài)的,當某個服務(wù)的地址改變時,電話簿上的地址就會改變,當某個服務(wù)不可用時,電話簿上的地址就會消失

這個動態(tài)的電話簿就是注冊中心。

注冊中心的實現(xiàn)方式有很多種,Zookeeper,Redis,Nocas等都可以實現(xiàn)

介紹一下用Zookeeper實現(xiàn)注冊中心的方式

zookeeper有兩種類型的節(jié)點,持久節(jié)點和臨時節(jié)點

當我們往zookeeper上注冊服務(wù)的時候,用的是臨時節(jié)點,這樣當服務(wù)斷開時,節(jié)點能被刪除

節(jié)點類型解釋
持久節(jié)點將節(jié)點創(chuàng)建為持久節(jié)點,數(shù)據(jù)會一直存儲在zookeeper服務(wù)器上,即使創(chuàng)建該節(jié)點的客戶端與服務(wù)端的會話關(guān)閉了,該節(jié)點依然不會被刪除
持久順序節(jié)點在持久節(jié)點的基礎(chǔ)上增加了節(jié)點有序的特性
臨時節(jié)點將節(jié)點創(chuàng)建為臨時節(jié)點,數(shù)據(jù)不會一直存儲在zookeeper服務(wù)器上,當創(chuàng)建該臨時節(jié)點的客戶端會話關(guān)閉時,該節(jié)點在相應(yīng)的zookeeper服務(wù)器上被刪除
臨時順序節(jié)點在臨時節(jié)點的基礎(chǔ)上增加了節(jié)點有序的特性

注冊中心全部掛掉該怎么通信?

當一臺zookeeper掛掉后,會自動切換到另一個zookeeper。全部掛掉也沒有關(guān)系,因為dubbo把映射關(guān)系保存了一份在本地,這個映射關(guān)系可以保存在Map中,也可以保存在文件中

新的服務(wù)注冊到注冊中心,本地緩存會更新嗎?

注冊了監(jiān)聽的話,當然會更新啊。當被監(jiān)聽的節(jié)點或者子節(jié)點發(fā)生變化的時候,會將相應(yīng)的內(nèi)容推送給監(jiān)聽的客戶端,你就可以更新本地的緩存了

Zookeeper中的事件如下

手寫一個RPC框架的方法教程

你可以把這個監(jiān)聽理解為分布式的觀察者模式

“手寫一個RPC框架的方法教程”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI