自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何手寫了一個(gè)RPC框架

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

 [[349775]]

介紹

當(dāng)開(kāi)發(fā)一個(gè)單體項(xiàng)目的時(shí)候,大家肯定都寫過(guò)類似的代碼。即服務(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)程過(guò)程調(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)用過(guò)程

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

  1. 調(diào)用方發(fā)送請(qǐng)求后由代理類將調(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)目,由簡(jiǎn)到難實(shí)現(xiàn)了一個(gè)rpc框架,歡迎star

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

生成代理類

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

我們看一下主流的做法。

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

這種方式最大的優(yōu)點(diǎn)就是能進(jìn)行多語(yǔ)言通信,即一份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é)碼操作類庫(kù)(如cglib,Javassist)

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

協(xié)議

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

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

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

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

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

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

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

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

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

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

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

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

序列化

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

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

序列化的方式有如下幾種

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

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

  1. 效率
  2. 空間開(kāi)銷
  3. 通用性和兼容性
  4. 安全性

通訊

常見(jiàn)的IO模型有如下四種

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

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

注冊(cè)中心

注冊(cè)中心的作用和電話簿類似。保存了服務(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)的電話簿就是注冊(cè)中心。

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

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

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

當(dāng)我們往zookeeper上注冊(cè)服務(wù)的時(shí)候,用的是臨時(shí)節(jié)點(diǎn),這樣當(dāng)服務(wù)斷開(kāi)時(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)有序的特性

注冊(cè)中心全部掛掉該怎么通信?

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

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

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

Zookeeper中的事件如下

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

小結(jié)

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

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

 

責(zé)任編輯:武曉燕 來(lái)源: Java識(shí)堂
相關(guān)推薦

2021-02-20 09:45:02

RPC框架Java

2024-08-02 09:49:35

Spring流程Tomcat

2022-03-01 11:38:51

RPC框架后端

2018-09-18 09:38:11

RPC遠(yuǎn)程調(diào)用網(wǎng)絡(luò)通信

2022-03-09 09:43:01

工具類線程項(xiàng)目

2020-01-09 11:11:35

RPC框架調(diào)用遠(yuǎn)程

2021-03-18 08:04:54

AQS工具CAS

2020-09-09 07:13:05

RPC框架

2022-11-07 18:36:03

組件RPC框架

2017-03-02 13:31:02

監(jiān)控系統(tǒng)

2024-01-02 12:17:44

Go傳統(tǒng)遠(yuǎn)程

2024-08-01 17:20:55

2022-04-11 09:15:44

中間件開(kāi)源

2019-06-17 08:21:06

RPC框架服務(wù)

2018-08-15 10:51:01

JavaSpring MVC框架

2021-08-12 00:03:37

JSStrview視圖

2020-08-17 08:20:16

iOSAOP框架

2021-12-07 06:55:17

節(jié)流函數(shù)Throttle

2009-10-01 09:19:45

PHP框架ZendFramewoCake

2022-03-24 07:57:58

Python水果忍者游戲
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)