您好,登錄后才能下訂單哦!
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 整體框架
如下:
關(guān)鍵技術(shù)
服務(wù)發(fā)布與訂閱:服務(wù)端使用 Zookeeper 注冊(cè)服務(wù)地址,客戶(hù)端從 Zookeeper 獲取可用的服務(wù)地址。
通信:使用 Netty 作為通信框架。
Spring:使用 Spring 配置服務(wù),加載 Bean,掃描注解。
動(dòng)態(tài)代理:客戶(hù)端使用代理模式透明化服務(wù)調(diào)用。
核心流程
服務(wù)消費(fèi)方(client)調(diào)用以本地調(diào)用方式調(diào)用服務(wù);
client stub 接收到調(diào)用后負(fù)責(zé)將方法、參數(shù)等組裝成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw;
client stub 找到服務(wù)地址,并將消息發(fā)送到服務(wù)端;
server stub 收到消息后進(jìn)行解碼;
server stub 根據(jù)解碼結(jié)果調(diào)用本地的服務(wù);
本地服務(wù)執(zhí)行并將結(jié)果返回給 server stub;
server stub 將返回結(jié)果打包成消息并發(fā)送至消費(fèi)方;
client stub 接收到消息,并進(jìn)行解碼;
RPC 的目標(biāo)就是要 2~8 這些步驟都封裝起來(lái),讓用戶(hù)對(duì)這些細(xì)節(jié)透明。JAVA 一般使用動(dòng)態(tài)代理方式實(shí)現(xiàn)遠(yuǎn)程調(diào)用。
消息編解碼
息數(shù)據(jù)結(jié)構(gòu)(接口名稱(chēng)+方法名+參數(shù)類(lèi)型和參數(shù)值+超時(shí)時(shí)間+ requestID)
客戶(hù)端的請(qǐng)求消息結(jié)構(gòu)一般需要包括以下內(nèi)容:
接口名稱(chēng):在我們的例子里接口名是“HelloWorldService”,如果不傳,服務(wù)端就不知道調(diào)用哪個(gè)接口了;
方法名:一個(gè)接口內(nèi)可能有很多方法,如果不傳方法名服務(wù)端也就不知道調(diào)用哪個(gè)方法;
參數(shù)類(lèi)型和參數(shù)值:參數(shù)類(lèi)型有很多,比如有 bool、int、long、double、string、map、list,甚至如 struct(class);以及相應(yīng)的參數(shù)值;
超時(shí)時(shí)間:
requestID,標(biāo)識(shí)唯一請(qǐng)求 id,在下面一節(jié)會(huì)詳細(xì)描述 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)題:
怎么讓當(dāng)前線程“暫停”,等結(jié)果回來(lái)后,再向后執(zhí)行?
通訊流程
requestID 生成-AtomicLong
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
將 處 理 結(jié) 果 的 回 調(diào) 對(duì) 象 callback , 存 放 到 全 局 ConcurrentHashMap 里 面put(requestID, callback);synchronized 獲取回調(diào)對(duì)象 callback 的鎖并自旋 wait
當(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 上的鎖并喚醒
提前一飽眼福,附完整 JVM pdf文檔和最新學(xué)習(xí)視頻
VX獲?。?3272413561
免責(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)容。