JAVA RPC:從上手到愛不釋手

文首嘀韧,思考一個(gè)問題:為什么需要 RPC 服務(wù)茎用?

在傳統(tǒng)的開發(fā)模式中,我們通常將系統(tǒng)的各個(gè)服務(wù)部署在單臺(tái)機(jī)器昌抠,隨著服務(wù)的擴(kuò)展患朱,這種方式已經(jīng)完全無法滿足系統(tǒng)大規(guī)模的擴(kuò)展需要,分布式系統(tǒng)由此誕生炊苫,在分布式系統(tǒng)中裁厅,最重要就是各個(gè)服務(wù)之間的 RPC 調(diào)用。

RPC 全稱 Remote Procedure Call——遠(yuǎn)程過程調(diào)用侨艾,它是一種通過網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請(qǐng)求服務(wù)执虹,而不需要了解底層網(wǎng)絡(luò)技術(shù)的方式。簡單一點(diǎn)就是:通過一定協(xié)議和方法使得調(diào)用遠(yuǎn)程計(jì)算機(jī)上的服務(wù)唠梨,就像調(diào)用本地服務(wù)一樣袋励。

通常來說,RPC 的實(shí)現(xiàn)方式有很多姻成,可以基于常見的 HTTP 協(xié)議插龄,也可以在TCP上層封裝自定義協(xié)議,常見的 Web Service 就是基于 HTTP 協(xié)議的 RPC科展,HTTP 協(xié)議的優(yōu)點(diǎn)是具有良好的跨平臺(tái)性,特別適合異構(gòu)系統(tǒng)較多的公司糠雨,但是由于 HTTP 報(bào)頭較為冗長才睹,性能較差,基于 TCP 協(xié)議的 RPC 可以建立長連接甘邀,速度和效率明顯琅攘,但是難度和復(fù)雜程度很高。

RPC 的誕生讓構(gòu)建分布式應(yīng)用更容易松邪,極大的擴(kuò)大系統(tǒng)的可擴(kuò)展性坞琴,容錯(cuò)性。為復(fù)雜業(yè)務(wù)邏輯的系統(tǒng)進(jìn)行服務(wù)化改造和高可用性升級(jí)提供了可能逗抑。

RPC 調(diào)用分類

RPC 調(diào)用的分類方式有很多種剧辐。

從通信協(xié)議層面可以分為:

基于 HTTP 協(xié)議的 RPC;

基于二進(jìn)制協(xié)議的 RPC邮府;

基于 TCP 協(xié)議的 RPC荧关。

從是否跨平臺(tái)可分為:

單語言 RPC,如 RMI, Remoting褂傀;

跨平臺(tái) RPC忍啤,如 google protobuffer, restful json,http XML仙辟。

從調(diào)用過程來看同波,可以分為同步通信RPC和異步通信RPC:

同步 RPC:指的是客戶端發(fā)起調(diào)用后鳄梅,必須等待調(diào)用執(zhí)行完成并返回結(jié)果;

異步 RPC:指客戶方調(diào)用后不關(guān)心執(zhí)行結(jié)果返回未檩,如果客戶端需要結(jié)果卫枝,可用通過提供異步 callback 回調(diào)獲取返回信息。大部分 RPC 框架都同時(shí)支持這兩種方式的調(diào)用讹挎。

RPC 框架結(jié)構(gòu)

一個(gè)完整的 RPC 框架的架構(gòu)主要模塊如圖所示校赤。

RPC 服務(wù)方的主要職責(zé)是提供服務(wù),供客戶端調(diào)用訪問筒溃,服務(wù)端會(huì)通過一個(gè)接收器接受客戶端的調(diào)用請(qǐng)求马篮,根據(jù)相應(yīng)的 RPC 協(xié)議進(jìn)行解碼獲取調(diào)用方法以及相關(guān)參數(shù),當(dāng)調(diào)用完成后怜奖,服務(wù)器端通過后臺(tái)處理模塊處理完成并將結(jié)果返回給客戶端浑测。

對(duì)于客戶端來說,服務(wù)調(diào)用完全透明歪玲,像調(diào)用本地服務(wù)一樣調(diào)用遠(yuǎn)程方法迁央,客戶端調(diào)用服務(wù)時(shí)候通過一個(gè)遠(yuǎn)程連接和服務(wù)端建立通道,并通過相應(yīng)的協(xié)議進(jìn)行編碼滥崩,將調(diào)用的方法和相關(guān)參數(shù)發(fā)送給服務(wù)方岖圈。

上 手 篇

RPC 模塊詳解

下面我們根據(jù)上面的RPC的架構(gòu)圖,對(duì)圖中的各個(gè)模塊進(jìn)行拆解钙皮,并解釋每個(gè)模塊的作用蜂科。

服務(wù)端(Server):RPC 服務(wù)的提供者,負(fù)責(zé)將 RPC 服務(wù)導(dǎo)出短条;

客戶端 (Client):RPC 服務(wù)的消費(fèi)者导匣,負(fù)責(zé)調(diào)用 RPC 服務(wù);

代理(Proxy):通過動(dòng)態(tài)代理茸时,提供對(duì)遠(yuǎn)程接口的代理實(shí)現(xiàn)贡定;

執(zhí)行器(Invoker):對(duì)于客戶端:主要負(fù)責(zé)服務(wù)調(diào)用的編碼,調(diào)用請(qǐng)求發(fā)送和等待結(jié)果返回可都;對(duì)于服務(wù)方:負(fù)責(zé)處理調(diào)用邏輯并返回調(diào)用結(jié)果缓待;

協(xié)議管理(Protocol):協(xié)議管理組件,負(fù)責(zé)整個(gè) RPC 通信協(xié)議的編/解碼汹粤;

連接端口(Connector):負(fù)責(zé)維持客戶方和服務(wù)方的長連接通道命斧;

后臺(tái)處理(Processor):負(fù)責(zé)整個(gè)調(diào)用服務(wù)中的管理調(diào)度,包括線程池嘱兼,分發(fā)国葬,異常處理等;

連接通道(Channel):客戶端和服務(wù)器端的數(shù)據(jù)傳輸通道。

具體到 JAVA 平臺(tái)來說汇四,其中的3,4通常使用動(dòng)態(tài)代理實(shí)現(xiàn)接奈,5,6,7,8使用 NIO 或者一些高性能 NIO 框架,如 mina,netty 實(shí)現(xiàn)通孽。

最簡單的 RPC JAVA 實(shí)現(xiàn)

在進(jìn)一步拆解了組件并劃分了職責(zé)之后序宦,這里以一個(gè)最簡單 Java RPC 框架實(shí)現(xiàn)為例,對(duì) RPC 具體邏輯進(jìn)行分析背苦。

RPC 框架服務(wù)發(fā)布代碼:

服務(wù)端發(fā)布服務(wù)的代碼如上互捌,首先校驗(yàn)傳入的端口和服務(wù)是否合法,然后開啟一個(gè) socket 監(jiān)聽行剂,這兒為了簡便秕噪,沒有采用 NIO 方式,同時(shí)直接采用 java 的序列化方式厚宰,將傳入的數(shù)據(jù)通過反射取出調(diào)用的方法和參數(shù)腌巾,本地執(zhí)行后將運(yùn)行結(jié)果通過 socket 套接字返回給客戶端。

RPC 框架服務(wù)調(diào)用代碼:

框架中客戶端調(diào)用的代碼中铲觉,首先校驗(yàn)對(duì)應(yīng)的端口和主機(jī)是否合法澈蝙,然后通過動(dòng)態(tài)代理生成一個(gè)代理對(duì)象,在代理對(duì)象的方法中撵幽,攔截調(diào)用灯荧,通過建立 socket 連接,將方法和參數(shù)傳遞到遠(yuǎn)端執(zhí)行并獲取遠(yuǎn)程執(zhí)行返回結(jié)果并齐。

RPC 調(diào)用測(cè)試:

如上圖所示漏麦,服務(wù)器端發(fā)布一個(gè)接口服務(wù) HelloService,客戶端成功通過 RPC 調(diào)用况褪。

思 考 篇

自定義 RPC 協(xié)議

協(xié)議頭

在上面的示例程序當(dāng)中,我們僅僅是完成了一個(gè)基本的遠(yuǎn)程調(diào)用更耻,并沒有實(shí)現(xiàn) RPC 框架中的很多組件功能测垛,從最簡單的代碼版本中我們可以發(fā)現(xiàn),發(fā)起一個(gè) RPC 調(diào)用秧均,需要傳輸?shù)淖罨緮?shù)據(jù)如下:

接口方法:包括接口的名字和相應(yīng)的方法名字食侮;

方法參數(shù):包括參數(shù)的類型和取值;

附件參數(shù)目胡,包括調(diào)用接口版本锯七,接口超時(shí)時(shí)間等等。

因此誉己,如果要自定義協(xié)議實(shí)現(xiàn) RPC眉尸,我們必須再協(xié)議的消息體中包含這部分?jǐn)?shù)據(jù),另外,我們需要定義一些協(xié)議元數(shù)據(jù)噪猾,這些元數(shù)據(jù)通常放在協(xié)議頭中霉祸,和包含必要參數(shù)的協(xié)議體一期組成了自定義消息。

元數(shù)據(jù)通常會(huì)包含以下字段袱蜡,大部分字段只需要1-2位:

magic: 魔數(shù)丝蹭,方便協(xié)議解碼

header_size: 協(xié)議頭大小,便于解碼坪蚁,同時(shí)可用用于處理TCP粘包問題

id :消息 id奔穿,用來標(biāo)示這次調(diào)用

version: 接口版本

type:消息類型,可用包括普通調(diào)用消息敏晤,心跳贱田,控制消息

status:消息狀態(tài),是否首次處理或者已經(jīng)處理

body_size: 消息體長度

serialize_type:消息體序列化類型

body:具體消息

具體消息

消息內(nèi)容在網(wǎng)絡(luò)上傳輸需要對(duì)其進(jìn)行編碼,這個(gè)編碼的過程就是序列化過程茵典,顯然湘换,對(duì)于網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù),在能夠保證信息足夠解碼的情況下统阿,序列化的大小越小彩倚,傳輸?shù)拈_銷就越小,效率就越高扶平,目前 JAVA 平臺(tái)常用的序列化方式有:xml帆离,json ,binary(包括 thrift; hession; kryo 等)结澄。

在 RPC 調(diào)用中我們推薦使用二進(jìn)制方式進(jìn)行序列化哥谷,在大部分的測(cè)試中,二進(jìn)制方式序列化具有相當(dāng)好的表現(xiàn)麻献,另外一個(gè)比較有意思的地方是们妥,每一次 JDK 版本的升級(jí),JAVA 自帶的序列化方式的效率都有提升勉吻。

服務(wù)端調(diào)用優(yōu)化

從前面的示例代碼中监婶,我們僅僅簡單的考慮了實(shí)現(xiàn)了組件中的服務(wù)端和客戶端,并沒有考慮效率問題齿桃,在一個(gè)完整的 RPC 框架中惑惶,我們需要考慮實(shí)現(xiàn)并優(yōu)化調(diào)用的每一個(gè)地方,同時(shí)短纵,為了符合業(yè)務(wù)需求带污,需要有很高的可靠性和容錯(cuò)機(jī)制。

具體來說香到,在動(dòng)態(tài)代理模塊鱼冀,我們不會(huì)采用 java 自帶的動(dòng)態(tài)接口报破,而是會(huì)采用一些性能更高的三方庫,在連接通道和連接模塊雷绢,我們會(huì)采用更優(yōu)秀的三方NIO泛烙,如 netty 來實(shí)現(xiàn),在后端處理模塊翘紊,我們也不會(huì)僅僅是執(zhí)行結(jié)果并返回蔽氨,要考慮更多的東西:

并發(fā)控制:當(dāng)多個(gè)請(qǐng)求并發(fā)處理的時(shí)候,如何管理和控制線程池和超時(shí)等待時(shí)間帆疟;

版本隔離:當(dāng)服務(wù)有多個(gè)版本的時(shí)候鹉究,如何讓不同的調(diào)用者能夠調(diào)用正確的服務(wù);

服務(wù)路由:當(dāng)服務(wù)提供者有多臺(tái)機(jī)器的時(shí)候踪宠,如何提高系統(tǒng)負(fù)載均衡自赔,路由到正確的服務(wù)端;

服務(wù)降級(jí):當(dāng)多個(gè)服務(wù)重要性有不同的時(shí)候柳琢,如果保證核心業(yè)務(wù)的穩(wěn)定性绍妨,適當(dāng)?shù)慕档头呛诵臉I(yè)務(wù)優(yōu)先級(jí);

服務(wù)監(jiān)控和報(bào)警:服務(wù)出現(xiàn)異常情況時(shí)候柬脸,運(yùn)維和對(duì)應(yīng)的系統(tǒng)負(fù)責(zé)人能夠第一時(shí)間得到告警和錯(cuò)誤信息他去。

以上的思考大部分要結(jié)合運(yùn)維層面一起考慮,但是 RPC 框架本身也要提供足夠的支持才能保證它足夠的健壯性倒堕。

需要注意的一些地方

雖然 RPC 有足夠多的優(yōu)點(diǎn)讓你去使用灾测,但是當(dāng)真正轉(zhuǎn)向服務(wù)化的時(shí)候,依然有很多需要考慮的地方:

網(wǎng)絡(luò)問題:本地調(diào)用無需考慮是否能夠執(zhí)行問題垦巴,網(wǎng)絡(luò)調(diào)用可能會(huì)因?yàn)楦鞣N外部網(wǎng)絡(luò)環(huán)境媳搪,端口攔截,IP 受限等可能情況導(dǎo)致無法成功執(zhí)行骤宣。所以 RPC 的服務(wù)端通常要考慮冪等性和容錯(cuò)性秦爆,接口需要較強(qiáng)的魯棒性設(shè)計(jì)。

異常處理:RPC 和本地服務(wù)最大的不同就是 RPC 服務(wù)存在分布式一致性問題憔披,當(dāng)服務(wù)沒有調(diào)用成功情況下鲜结,本地和遠(yuǎn)程的服務(wù)可能處于一個(gè)不一致的狀態(tài),如何進(jìn)行異常處理和事物的回滾機(jī)制也是一個(gè)需要考慮的問題活逆,是需要保障強(qiáng)一致性和最終一致性通常取決于具體的業(yè)務(wù)需求。

由于網(wǎng)絡(luò)原因拗胜,RPC 服務(wù)通常會(huì)被本地服務(wù)處理慢一個(gè)數(shù)量級(jí)蔗候,在比較輕量級(jí)的業(yè)務(wù)和并發(fā)量很小的情況下,并不需要 RPC 服務(wù)埂软,引入 RPC 服務(wù)后锈遥,無論是系統(tǒng)的調(diào)試纫事,還是線上問題分析都會(huì)變得非常復(fù)雜,是否引入也需要權(quán)衡相關(guān)利弊所灸。

文末小結(jié)

本文簡單的介紹了 RPC 的基本知識(shí)和相關(guān)分析以供拋磚引玉丽惶,進(jìn)一步的學(xué)習(xí)可以參考當(dāng)前最主流的一些 RPC 框架,如dubbo, protobuff ,thrift 通過對(duì)其源碼的深入學(xué)習(xí)爬立,相信能獲益匪淺钾唬。


本文作者:陳瑜(點(diǎn)融黑幫),現(xiàn)任點(diǎn)融技術(shù)部成都后端開發(fā)侠驯,曾就職于阿里巴巴抡秆。目前專注于Java技術(shù),架構(gòu)吟策,愛好閱讀儒士,旅游,運(yùn)動(dòng)檩坚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末着撩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子匾委,更是在濱河造成了極大的恐慌拖叙,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剩檀,死亡現(xiàn)場(chǎng)離奇詭異憋沿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)沪猴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門辐啄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人运嗜,你說我怎么就攤上這事壶辜。” “怎么了担租?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵砸民,是天一觀的道長。 經(jīng)常有香客問我奋救,道長岭参,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任尝艘,我火速辦了婚禮演侯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘背亥。我一直安慰自己秒际,他們只是感情好悬赏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著娄徊,像睡著了一般闽颇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寄锐,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天兵多,我揣著相機(jī)與錄音,去河邊找鬼锐峭。 笑死中鼠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沿癞。 我是一名探鬼主播援雇,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼椎扬!你這毒婦竟也來了惫搏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蚕涤,失蹤者是張志新(化名)和其女友劉穎筐赔,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體揖铜,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茴丰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了天吓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贿肩。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖龄寞,靈堂內(nèi)的尸體忽然破棺而出汰规,到底是詐尸還是另有隱情,我是刑警寧澤物邑,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布溜哮,位于F島的核電站,受9級(jí)特大地震影響色解,放射性物質(zhì)發(fā)生泄漏茂嗓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一科阎、第九天 我趴在偏房一處隱蔽的房頂上張望在抛。 院中可真熱鬧,春花似錦萧恕、人聲如沸刚梭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽朴读。三九已至,卻和暖如春走趋,著一層夾襖步出監(jiān)牢的瞬間衅金,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工簿煌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留氮唯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓姨伟,卻偏偏與公主長得像惩琉,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夺荒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容