日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
如何手寫了一個(gè)RPC框架

介紹

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長期合作伙伴,公司提供的服務(wù)項(xiàng)目有:域名申請、雅安服務(wù)器托管、營銷軟件、網(wǎng)站建設(shè)、于田網(wǎng)站維護(hù)、網(wǎng)站推廣。

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

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

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

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

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

RPC的調(diào)用過程

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

  1. 調(diào)用方發(fā)送請求后由代理類將調(diào)用的方法,參數(shù)組裝成能進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw
  2. 調(diào)用方將消息體發(fā)送到提供方
  3. 提供方將消息進(jìn)行解碼,得到調(diào)用的參數(shù)
  4. 提供方反射執(zhí)行相應(yīng)的方法,并將結(jié)果返回

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

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

生成代理類

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

我們看一下主流的做法。

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

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

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

  1. JDK動(dòng)態(tài)代理(實(shí)現(xiàn)InvocationHandler接口)
  2. 字節(jié)碼操作類庫(如cglib,Javassist)

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

協(xié)議

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

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

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

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

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

  1. 定長協(xié)議,協(xié)議內(nèi)容長度固定,如讀取到50個(gè)byte就開始decode操作,可以參考Netty的FixedLengthFrameDecoder
  2. 特殊結(jié)束符,定義一個(gè)消息結(jié)束的分隔符,如讀到\n,表示一個(gè)數(shù)據(jù)讀取完畢了,沒有讀到就一直讀,可以參考Netty的DelimiterBasedFrameDecoder
  3. 變長協(xié)議(協(xié)議頭+協(xié)議體),用一個(gè)定長來表示消息體的長度,剩下的內(nèi)容為消息體,如果你愿意的話,協(xié)議頭還會(huì)放一些常用的屬性,Http協(xié)議的Header就是協(xié)議頭,如content-type,content-length等??梢詤⒖糔etty的DelimiterBasedFrameDecoder

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

每個(gè)位代表的含義如下

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)用程序中則會(huì)被封裝成對象,如Dubbo將請求封裝成Request,將響應(yīng)封裝成Response

前面我們說過網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)必須是二進(jìn)制數(shù)據(jù),但調(diào)用方的入?yún)⒑吞峁┓降姆祷刂刀际菍ο?,因此需要序列化和反序列化的過程

序列化的方式有如下幾種

  1. JDK原生序列化
  2. JSON
  3. Protobuf
  4. Kryo
  5. Hessian2
  6. MessagePack

我們選擇序列化的方式時(shí),主要考慮如下幾個(gè)因素

  1. 效率
  2. 空間開銷
  3. 通用性和兼容性
  4. 安全性

通訊

常見的IO模型有如下四種

  1. 同步阻塞IO(Blocking IO)
  2. 同步非阻塞IO(Non-blocking IO)
  3. IO多路復(fù)用(IO Multiplexing)
  4. 異步IO(Asynchronous IO)

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

注冊中心

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

更重要的是這個(gè)電話簿是動(dòng)態(tài)的,當(dāng)某個(gè)服務(wù)的地址改變時(shí),電話簿上的地址就會(huì)改變,當(dāng)某個(gè)服務(wù)不可用時(shí),電話簿上的地址就會(huì)消失

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

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

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

zookeeper有兩種類型的節(jié)點(diǎn),持久節(jié)點(diǎn)和臨時(shí)節(jié)點(diǎn)

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

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

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

當(dāng)一臺(tái)zookeeper掛掉后,會(huì)自動(dòng)切換到另一個(gè)zookeeper。全部掛掉也沒有關(guān)系,因?yàn)閐ubbo把映射關(guān)系保存了一份在本地,這個(gè)映射關(guān)系可以保存在Map中,也可以保存在文件中

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

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

Zookeeper中的事件如下

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

小結(jié)

當(dāng)然一個(gè)成熟的RPC框架還得考慮很多內(nèi)容,例如路由策略,異常重試,監(jiān)控,異步調(diào)用等,和主流程相關(guān)度不大,就不多做介紹了

本文轉(zhuǎn)載自微信公眾號「Java識(shí)堂」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Java識(shí)堂公眾號。


本文標(biāo)題:如何手寫了一個(gè)RPC框架
網(wǎng)頁URL:http://m.5511xx.com/article/dhisshh.html