Netty RPC 實現(xiàn)
概念
Netty是由JBOSS提供的一個java開源框架,現(xiàn)為 Github上的獨立項目。Netty提供異步的、事件驅動的網(wǎng)絡應用程序框架和工具,用以快速開發(fā)高性能、高可靠性的網(wǎng)絡服務器和客戶端程序。
也就是說,Netty 是一個基于NIO的客戶、服務器端編程框架,使用Netty 可以確保你快速和簡單的開發(fā)出一個網(wǎng)絡應用,例如實現(xiàn)了某種協(xié)議的客戶、服務端應用。Netty相當于簡化和流線化了網(wǎng)絡應用的編程開發(fā)過程,例如:基于TCP和UDP的socket服務開發(fā)。
"快速"和"簡單"并不用產(chǎn)生維護性或性能上的問題。Netty 是一個吸收了多種協(xié)議(包括FTP、SMTP、HTTP等各種二進制文本協(xié)議)的實現(xiàn)經(jīng)驗,并經(jīng)過相當精心設計的項目。最終,Netty 成功的找到了一種方式,在保證易于開發(fā)的同時還保證了其應用的性能,穩(wěn)定性和伸縮性。
RPC,即 Remote Procedure Call(遠程過程調用),調用遠程計算機上的服務,就像調用本地服務一樣。RPC 可以很好的解耦系統(tǒng),如 WebService 就是一種基于 Http 協(xié)議的 RPC。這個 RPC 整體框架
如下:
關鍵技術
服務發(fā)布與訂閱:服務端使用 Zookeeper 注冊服務地址,客戶端從 Zookeeper 獲取可用的服務地址。
通信:使用 Netty 作為通信框架。
Spring:使用 Spring 配置服務,加載 Bean,掃描注解。
動態(tài)代理:客戶端使用代理模式透明化服務調用。
核心流程
服務消費方(client)調用以本地調用方式調用服務;
client stub 接收到調用后負責將方法、參數(shù)等組裝成能夠進行網(wǎng)絡傳輸?shù)南Ⅲw;
client stub 找到服務地址,并將消息發(fā)送到服務端;
server stub 收到消息后進行解碼;
server stub 根據(jù)解碼結果調用本地的服務;
本地服務執(zhí)行并將結果返回給 server stub;
server stub 將返回結果打包成消息并發(fā)送至消費方;
client stub 接收到消息,并進行解碼;
RPC 的目標就是要 2~8 這些步驟都封裝起來,讓用戶對這些細節(jié)透明。JAVA 一般使用動態(tài)代理方式實現(xiàn)遠程調用。
消息編解碼
息數(shù)據(jù)結構(接口名稱+方法名+參數(shù)類型和參數(shù)值+超時時間+ requestID)
客戶端的請求消息結構一般需要包括以下內(nèi)容:
接口名稱:在我們的例子里接口名是“HelloWorldService”,如果不傳,服務端就不知道調用哪個接口了;
方法名:一個接口內(nèi)可能有很多方法,如果不傳方法名服務端也就不知道調用哪個方法;
參數(shù)類型和參數(shù)值:參數(shù)類型有很多,比如有 bool、int、long、double、string、map、list,甚至如 struct(class);以及相應的參數(shù)值;
超時時間:
requestID,標識唯一請求 id,在下面一節(jié)會詳細描述 requestID 的用處。
序列化
目前互聯(lián)網(wǎng)公司廣泛使用 Protobuf、Thrift、Avro 等成熟的序列化解決方案來搭建 RPC 框架,這些都是久經(jīng)考驗的解決方案。
通訊過程
核心問題(線程暫停、消息亂序)
如果使用 netty 的話,一般會用 channel.writeAndFlush()方法來發(fā)送消息二進制串,這個方法調用后對于整個遠程調用(從發(fā)出請求到接收到結果)來說是一個異步的,即對于當前線程來說,將請求發(fā)送出來后,線程就可以往后執(zhí)行了,至于服務端的結果,是服務端處理完成后,再以消息的形式發(fā)送給客戶端的。于是這里出現(xiàn)以下兩個問題:
怎么讓當前線程“暫?!保冉Y果回來后,再向后執(zhí)行?
通訊流程
requestID 生成-AtomicLong
client 線程每次通過 socket 調用一次遠程接口前,生成一個唯一的 ID,即 requestID(requestID 必需保證在一個 Socket 連接里面是唯一的),一般常常使用 AtomicLong從 0 開始累計數(shù)字生成唯一 ID;存放回調對象 callback 到全局 ConcurrentHashMap
將 處 理 結 果 的 回 調 對 象 callback , 存 放 到 全 局 ConcurrentHashMap 里 面put(requestID, callback);synchronized 獲取回調對象 callback 的鎖并自旋 wait
當線程調用 channel.writeAndFlush()發(fā)送消息后,緊接著執(zhí)行 callback 的 get()方法試圖獲取遠程返回的結果。在 get()內(nèi)部,則使用 synchronized 獲取回調對象 callback 的鎖,再先檢測是否已經(jīng)獲取到結果,如果沒有,然后調用 callback 的 wait()方法,釋放callback 上的鎖,讓當前線程處于等待狀態(tài)。監(jiān)聽消息的線程收到消息,找到 callback 上的鎖并喚醒
提前一飽眼福,附完整 JVM pdf文檔和最新學習視頻
VX獲?。?3272413561
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。