一不小心實(shí)現(xiàn)了RPC

以下文章來(lái)源于公眾號(hào)crossoverJie ,作者crossoverJie

前言

image

隨著最近關(guān)注 cim 項(xiàng)目的人越發(fā)增多剿骨,導(dǎo)致提的問(wèn)題以及 Bug 也在增加代芜,在修復(fù)問(wèn)題的過(guò)程中難免代碼潔癖又上來(lái)了。

看著一兩年前寫(xiě)的東西總是懷疑這真的是出自自己手里嘛浓利?有些地方實(shí)在忍不住了便開(kāi)始了漫漫重構(gòu)之路挤庇。

前后對(duì)比

在開(kāi)始之前先簡(jiǎn)單介紹一下 cim 這個(gè)項(xiàng)目钞速,下面是它的架構(gòu)圖:

image

簡(jiǎn)單來(lái)說(shuō)就是一個(gè) IM 即時(shí)通訊系統(tǒng),主要有以下部分組成:

  • IM-server 自然就是服務(wù)端了嫡秕,用于和客戶端保持長(zhǎng)連接渴语。

  • IM-client 客戶端,可以簡(jiǎn)單認(rèn)為是類(lèi)似于的 QQ 這樣的客戶端工具昆咽;當(dāng)然功能肯定沒(méi)那么豐富驾凶,只提供了一些簡(jiǎn)單消息發(fā)送、接收的功能掷酗。

  • Route 路由服務(wù)调违,主要用于客戶端鑒權(quán)、消息的轉(zhuǎn)發(fā)等汇在;提供一些 http 接口翰萨,可以用于查看系統(tǒng)狀態(tài)、在線人數(shù)等功能糕殉。

當(dāng)然服務(wù)端、路由都可以水平擴(kuò)展殖告。


image

這是一個(gè)消息發(fā)送的流程圖阿蝶,假設(shè)現(xiàn)在部署了兩個(gè)服務(wù)端 A、B 和一個(gè)路由服務(wù)黄绩;其中 ClientAClientB 分別和服務(wù)端 A羡洁、B 保持了長(zhǎng)連接。

當(dāng) ClientAClientB 發(fā)送一個(gè) hello world 時(shí)爽丹,整個(gè)的消息流轉(zhuǎn)如圖所示:

  1. 先通過(guò) http 將消息發(fā)送到 Route 服務(wù)筑煮。

  2. 路由服務(wù)得知 ClientB 是連接在 ServerB 上;于是再通過(guò) http 將消息發(fā)送給 ServerB粤蝎。

  3. 最終 ServerB 將消息通過(guò)與 ClientB 的長(zhǎng)連接通道 push 下去真仲,至此消息發(fā)送成功。

這里我截取了 ClientARoute 發(fā)起請(qǐng)求的代碼:

image

可以看到這就是利用 okhttp 發(fā)起了一個(gè) http 請(qǐng)求初澎,這樣雖然能實(shí)現(xiàn)功能秸应,但其實(shí)并不優(yōu)雅。

舉個(gè)例子:假設(shè)我們需要對(duì)接支付寶的接口碑宴,這里發(fā)送一個(gè) http 請(qǐng)求自然是沒(méi)問(wèn)題软啼;但對(duì)于支付寶內(nèi)部各部門(mén)直接互相調(diào)用接口時(shí)那就不應(yīng)該再使用原始的 http 請(qǐng)求了。

應(yīng)該是由服務(wù)提供方提供一個(gè) api 包延柠,服務(wù)消費(fèi)者只需要依賴(lài)這個(gè)包就可以實(shí)現(xiàn)接口調(diào)用祸挪。

當(dāng)然最終使用的是 http、還是自定義私有協(xié)議都可以贞间。

也類(lèi)似于我們?cè)谑褂?Dubbo 或者是 SpringCloud 時(shí)贿条,通常是直接依賴(lài)一個(gè) api 包雹仿,便可以像調(diào)用一個(gè)本地方法一樣調(diào)用遠(yuǎn)程服務(wù)了,并且完全屏蔽了底層細(xì)節(jié)闪唆,不管是使用的 http 還是 其他私有協(xié)議都沒(méi)關(guān)系盅粪,對(duì)于調(diào)用者來(lái)說(shuō)完全不關(guān)心。

這么一說(shuō)是不是有內(nèi)味了悄蕾,這不就是 RPC 的官方解釋嘛票顾。

對(duì)應(yīng)到這里也是同樣的道理, Client 帆调、 Route奠骄、 Server 本質(zhì)上都是一個(gè)系統(tǒng),他們互相的接口調(diào)用也應(yīng)當(dāng)是走 RPC 才合理番刊。

所以我重構(gòu)之后的變成這樣了:

image

是不是代碼也簡(jiǎn)潔了許多含鳞,就和調(diào)用本地方法一樣了,而且這樣也有幾個(gè)好處:

  • 完全屏蔽了底層細(xì)節(jié)芹务,可以更好的實(shí)現(xiàn)業(yè)務(wù)及維護(hù)代碼蝉绷。

  • 即便是服務(wù)提供方修改了參數(shù),在編譯期間就能很快發(fā)現(xiàn)枣抱,而像之前那樣調(diào)用是完全不知情的熔吗,所以也增加了風(fēng)險(xiǎn)。

繞不開(kāi)的動(dòng)態(tài)代理

下面來(lái)聊聊具體是如何實(shí)現(xiàn)的佳晶。

要想做到對(duì)調(diào)用者無(wú)感知桅狠,就得創(chuàng)建一個(gè)接口的代理對(duì)象;在這個(gè)代理對(duì)象中實(shí)現(xiàn)編碼轿秧、調(diào)用中跌、解碼的過(guò)程。

image

對(duì)應(yīng)到此處其實(shí)就是創(chuàng)建一個(gè) routeApi 的代理對(duì)象菇篡,關(guān)鍵就是這段代碼:

  1. RouteApi routeApi = new ProxyManager<>(RouteApi.class, routeUrl, okHttpClient).getInstance();

完整源碼如下:

image

其中的 getInstance() 函數(shù)就是返回了需要被代理的接口對(duì)象漩符;而其中的 ProxyInvocation則是一個(gè)實(shí)現(xiàn)了 InvocationHandler 接口的類(lèi),這套代碼就是利用 JDK 實(shí)現(xiàn)動(dòng)態(tài)代理的三板斧逸贾。

image

查看 ProxyInvocation 的源碼會(huì)發(fā)現(xiàn)當(dāng)我們調(diào)用被代理接口的任意一個(gè)方法時(shí)陨仅,都會(huì)執(zhí)行這里的 invoke()方法。

invoke() 方法自然就實(shí)現(xiàn)了上圖中提到的:編碼铝侵、遠(yuǎn)程調(diào)用灼伤、解碼的過(guò)程;相信大家很容易看明白咪鲜,由于不是本次探討的重點(diǎn)就不過(guò)多介紹了狐赡。

總結(jié)

其實(shí)理解這些就也就很容易看懂 Dubbo 這類(lèi) RPC 框架的核心源碼了,總體的思路也是類(lèi)似的疟丙,只不過(guò)使用的私有協(xié)議颖侄,所以在編解碼時(shí)會(huì)有所不同鸟雏。

所以大家要是想自己動(dòng)手實(shí)現(xiàn)一個(gè) RPC 框架,不妨參考這個(gè)思路試試览祖,當(dāng)用自己寫(xiě)的代碼跑通一個(gè) RPChelloworld 時(shí)的感覺(jué)是和自己整合了一個(gè) Dubbo孝鹊、 SpringCloud 這樣的第三方框架的感覺(jué)是完全不同的。

本文的所有源碼:

https://github.com/crossoverJie/cim

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末展蒂,一起剝皮案震驚了整個(gè)濱河市又活,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锰悼,老刑警劉巖柳骄,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異箕般,居然都是意外死亡耐薯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)丝里,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)曲初,“玉大人,你說(shuō)我怎么就攤上這事杯聚「闯猓” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵械媒,是天一觀的道長(zhǎng)显熏。 經(jīng)常有香客問(wèn)我趾访,道長(zhǎng)纷责,這世上最難降的妖魔是什么下梢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任傲绣,我火速辦了婚禮世澜,結(jié)果婚禮上暑诸,老公的妹妹穿的比我還像新娘县恕。我一直安慰自己惨缆,他們只是感情好糜值,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著坯墨,像睡著了一般寂汇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捣染,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天骄瓣,我揣著相機(jī)與錄音,去河邊找鬼耍攘。 笑死榕栏,一個(gè)胖子當(dāng)著我的面吹牛畔勤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扒磁,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼庆揪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了妨托?” 一聲冷哼從身側(cè)響起缸榛,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎始鱼,沒(méi)想到半個(gè)月后仔掸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡医清,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年起暮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片会烙。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡负懦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柏腻,到底是詐尸還是另有隱情纸厉,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布五嫂,位于F島的核電站颗品,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沃缘。R本人自食惡果不足惜躯枢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望槐臀。 院中可真熱鬧锄蹂,春花似錦、人聲如沸水慨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)晰洒。三九已至朝抖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間欢顷,已是汗流浹背槽棍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炼七。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓缆巧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親豌拙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陕悬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354