您好,登錄后才能下訂單哦!
本篇內(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調(diào)用的過程如下
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
調(diào)用方發(fā)送請求后由代理類將調(diào)用的方法,參數(shù)組裝成能進行網(wǎng)絡(luò)傳輸?shù)南Ⅲw
調(diào)用方將消息體發(fā)送到提供方
提供方將消息進行解碼,得到調(diào)用的參數(shù)
提供方反射執(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中生成代理類的方式有如下幾種
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
JDK動態(tài)代理(實現(xiàn)InvocationHandler接口)
字節(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個例子
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
定長協(xié)議,協(xié)議內(nèi)容長度固定,如讀取到50個byte就開始decode操作,可以參考Netty的FixedLengthFrameDecoder
特殊結(jié)束符,定義一個消息結(jié)束的分隔符,如讀到\n,表示一個數(shù)據(jù)讀取完畢了,沒有讀到就一直讀,可以參考Netty的DelimiterBasedFrameDecoder
變長協(xié)議(協(xié)議頭+協(xié)議體),用一個定長來表示消息體的長度,剩下的內(nèi)容為消息體,如果你愿意的話,協(xié)議頭還會放一些常用的屬性,Http協(xié)議的Header就是協(xié)議頭,如content-type,content-length等。可以參考Netty的DelimiterBasedFrameDecoder
Dubbo通過自定義協(xié)議來進行通訊,協(xié)議頭格式如下
每個位代表的含義如下
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
前面我們說過網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)必須是二進制數(shù)據(jù),但調(diào)用方的入?yún)⒑吞峁┓降姆祷刂刀际菍ο螅虼诵枰蛄谢头葱蛄谢倪^程
序列化的方式有如下幾種
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
JDK原生序列化
JSON
Protobuf
Kryo
Hessian2
MessagePack
我們選擇序列化的方式時,主要考慮如下幾個因素
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
效率
空間開銷
通用性和兼容性
安全性
通訊
常見的IO模型有如下四種
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
同步阻塞IO(Blocking IO)
同步非阻塞IO(Non-blocking IO)
IO多路復用(IO Multiplexing)
異步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中的事件如下
你可以把這個監(jiān)聽理解為分布式的觀察者模式
“手寫一個RPC框架的方法教程”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
免責聲明:本站發(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)容。