溫馨提示×

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

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

Netty RPC的實(shí)現(xiàn)流程

發(fā)布時(shí)間:2020-06-16 15:29:23 來(lái)源:億速云 閱讀:377 作者:元一 欄目:編程語(yǔ)言

Netty RPC 實(shí)現(xiàn)
概念

Netty是由JBOSS提供的一個(gè)java開(kāi)源框架,現(xiàn)為 Github上的獨(dú)立項(xiàng)目。Netty提供異步的、事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架和工具,用以快速開(kāi)發(fā)高性能、高可靠性的網(wǎng)絡(luò)服務(wù)器和客戶(hù)端程序。

也就是說(shuō),Netty 是一個(gè)基于NIO的客戶(hù)、服務(wù)器端編程框架,使用Netty 可以確保你快速和簡(jiǎn)單的開(kāi)發(fā)出一個(gè)網(wǎng)絡(luò)應(yīng)用,例如實(shí)現(xiàn)了某種協(xié)議的客戶(hù)、服務(wù)端應(yīng)用。Netty相當(dāng)于簡(jiǎn)化和流線化了網(wǎng)絡(luò)應(yīng)用的編程開(kāi)發(fā)過(guò)程,例如:基于TCP和UDP的socket服務(wù)開(kāi)發(fā)。

"快速"和"簡(jiǎn)單"并不用產(chǎn)生維護(hù)性或性能上的問(wèn)題。Netty 是一個(gè)吸收了多種協(xié)議(包括FTP、SMTP、HTTP等各種二進(jìn)制文本協(xié)議)的實(shí)現(xiàn)經(jīng)驗(yàn),并經(jīng)過(guò)相當(dāng)精心設(shè)計(jì)的項(xiàng)目。最終,Netty 成功的找到了一種方式,在保證易于開(kāi)發(fā)的同時(shí)還保證了其應(yīng)用的性能,穩(wěn)定性和伸縮性。

RPC,即 Remote Procedure Call(遠(yuǎn)程過(guò)程調(diào)用),調(diào)用遠(yuǎn)程計(jì)算機(jī)上的服務(wù),就像調(diào)用本地服務(wù)一樣。RPC 可以很好的解耦系統(tǒng),如 WebService 就是一種基于 Http 協(xié)議的 RPC。這個(gè) RPC 整體框架

如下:

Netty RPC的實(shí)現(xiàn)流程
關(guān)鍵技術(shù)

  1. 服務(wù)發(fā)布與訂閱:服務(wù)端使用 Zookeeper 注冊(cè)服務(wù)地址,客戶(hù)端從 Zookeeper 獲取可用的服務(wù)地址。

  2. 通信:使用 Netty 作為通信框架。

  3. Spring:使用 Spring 配置服務(wù),加載 Bean,掃描注解。

  4. 動(dòng)態(tài)代理:客戶(hù)端使用代理模式透明化服務(wù)調(diào)用。

  5. 消息編解碼:使用 Protostuff 序列化和反序列化消息。

核心流程

  1. 服務(wù)消費(fèi)方(client)調(diào)用以本地調(diào)用方式調(diào)用服務(wù);

  2. client stub 接收到調(diào)用后負(fù)責(zé)將方法、參數(shù)等組裝成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw;

  3. client stub 找到服務(wù)地址,并將消息發(fā)送到服務(wù)端;

  4. server stub 收到消息后進(jìn)行解碼;

  5. server stub 根據(jù)解碼結(jié)果調(diào)用本地的服務(wù);

  6. 本地服務(wù)執(zhí)行并將結(jié)果返回給 server stub;

  7. server stub 將返回結(jié)果打包成消息并發(fā)送至消費(fèi)方;

  8. client stub 接收到消息,并進(jìn)行解碼;

  9. 服務(wù)消費(fèi)方得到最終結(jié)果。

RPC 的目標(biāo)就是要 2~8 這些步驟都封裝起來(lái),讓用戶(hù)對(duì)這些細(xì)節(jié)透明。JAVA 一般使用動(dòng)態(tài)代理方式實(shí)現(xiàn)遠(yuǎn)程調(diào)用。

Netty RPC的實(shí)現(xiàn)流程
消息編解碼

息數(shù)據(jù)結(jié)構(gòu)(接口名稱(chēng)+方法名+參數(shù)類(lèi)型和參數(shù)值+超時(shí)時(shí)間+ requestID)
客戶(hù)端的請(qǐng)求消息結(jié)構(gòu)一般需要包括以下內(nèi)容:

  1. 接口名稱(chēng):在我們的例子里接口名是“HelloWorldService”,如果不傳,服務(wù)端就不知道調(diào)用哪個(gè)接口了;

  2. 方法名:一個(gè)接口內(nèi)可能有很多方法,如果不傳方法名服務(wù)端也就不知道調(diào)用哪個(gè)方法;

  3. 參數(shù)類(lèi)型和參數(shù)值:參數(shù)類(lèi)型有很多,比如有 bool、int、long、double、string、map、list,甚至如 struct(class);以及相應(yīng)的參數(shù)值;

  4. 超時(shí)時(shí)間:

  5. requestID,標(biāo)識(shí)唯一請(qǐng)求 id,在下面一節(jié)會(huì)詳細(xì)描述 requestID 的用處。

  6. 服務(wù)端返回的消息 : 一般包括以下內(nèi)容。返回值+狀態(tài) code+requestID

序列化

目前互聯(lián)網(wǎng)公司廣泛使用 Protobuf、Thrift、Avro 等成熟的序列化解決方案來(lái)搭建 RPC 框架,這些都是久經(jīng)考驗(yàn)的解決方案。

通訊過(guò)程

核心問(wèn)題(線程暫停、消息亂序)

如果使用 netty 的話,一般會(huì)用 channel.writeAndFlush()方法來(lái)發(fā)送消息二進(jìn)制串,這個(gè)方法調(diào)用后對(duì)于整個(gè)遠(yuǎn)程調(diào)用(從發(fā)出請(qǐng)求到接收到結(jié)果)來(lái)說(shuō)是一個(gè)異步的,即對(duì)于當(dāng)前線程來(lái)說(shuō),將請(qǐng)求發(fā)送出來(lái)后,線程就可以往后執(zhí)行了,至于服務(wù)端的結(jié)果,是服務(wù)端處理完成后,再以消息的形式發(fā)送給客戶(hù)端的。于是這里出現(xiàn)以下兩個(gè)問(wèn)題:

  1. 怎么讓當(dāng)前線程“暫停”,等結(jié)果回來(lái)后,再向后執(zhí)行?

  2. 如果有多個(gè)線程同時(shí)進(jìn)行遠(yuǎn)程方法調(diào)用,這時(shí)建立在 client server 之間的 socket 連接上會(huì)有很多雙方發(fā)送的消息傳遞,前后順序也可能是隨機(jī)的,server 處理完結(jié)果后,將結(jié)果消息發(fā)送給 client,client 收到很多消息,怎么知道哪個(gè)消息結(jié)果是原先哪個(gè)線程調(diào)用的?如下圖所示,線程 A 和線程 B 同時(shí)向 client socket 發(fā)送請(qǐng)求 requestA 和 requestB,socket 先后將 requestB 和 requestA 發(fā)送至 server,而 server 可能將 responseB 先返回,盡管 requestB 請(qǐng)求到達(dá)時(shí)間更晚。我們需要一種機(jī)制保證 responseA 丟給****ThreadA,responseB 丟給 ThreadB。

Netty RPC的實(shí)現(xiàn)流程
通訊流程

requestID 生成-AtomicLong

  1. client 線程每次通過(guò) socket 調(diào)用一次遠(yuǎn)程接口前,生成一個(gè)唯一的 ID,即 requestID(requestID 必需保證在一個(gè) Socket 連接里面是唯一的),一般常常使用 AtomicLong從 0 開(kāi)始累計(jì)數(shù)字生成唯一 ID;存放回調(diào)對(duì)象 callback 到全局 ConcurrentHashMap

  2. 將 處 理 結(jié) 果 的 回 調(diào) 對(duì) 象 callback , 存 放 到 全 局 ConcurrentHashMap 里 面put(requestID, callback);synchronized 獲取回調(diào)對(duì)象 callback 的鎖并自旋 wait

  3. 當(dāng)線程調(diào)用 channel.writeAndFlush()發(fā)送消息后,緊接著執(zhí)行 callback 的 get()方法試圖獲取遠(yuǎn)程返回的結(jié)果。在 get()內(nèi)部,則使用 synchronized 獲取回調(diào)對(duì)象 callback 的鎖,再先檢測(cè)是否已經(jīng)獲取到結(jié)果,如果沒(méi)有,然后調(diào)用 callback 的 wait()方法,釋放callback 上的鎖,讓當(dāng)前線程處于等待狀態(tài)。監(jiān)聽(tīng)消息的線程收到消息,找到 callback 上的鎖并喚醒

  4. 服務(wù)端接收到請(qǐng)求并處理后,將 response 結(jié)果(此結(jié)果中包含了前面的 requestID)發(fā)送給客戶(hù)端,客戶(hù)端 socket 連接上專(zhuān)門(mén)監(jiān)聽(tīng)消息的線程收到消息,分析結(jié)果,取到requestID , 再 從 前 面 的 ConcurrentHashMap 里 面 get(requestID) , 從 而 找 到callback 對(duì)象,再用 synchronized 獲取 callback 上的鎖,將方法調(diào)用結(jié)果設(shè)置到callback 對(duì)象里,再調(diào)用 callback.notifyAll()喚醒前面處于等待狀態(tài)的線程。
    Netty RPC的實(shí)現(xiàn)流程
    未完待續(xù)…………

提前一飽眼福,附完整 JVM pdf文檔和最新學(xué)習(xí)視頻
VX獲?。?3272413561
Netty RPC的實(shí)現(xiàn)流程
Netty RPC的實(shí)現(xiàn)流程
Netty RPC的實(shí)現(xiàn)流程

向AI問(wèn)一下細(xì)節(jié)

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

AI