隨着最近關注 cim 項目的人愈加增多,致使提的問題以及 Bug 也在增長,在修復問題的過程當中不免代碼潔癖又上來了。java
看着一兩年前寫的東西老是懷疑這真的是出自本身手裏嘛?有些地方實在忍不住了便開始了漫漫重構之路。git
在開始以前先簡單介紹一下 cim
這個項目,下面是它的架構圖:github
簡單來講就是一個 IM 即時通信系統,主要有如下部分組成:api
IM-server
天然就是服務端了,用於和客戶端保持長鏈接。IM-client
客戶端,能夠簡單認爲是相似於的 QQ 這樣的客戶端工具;固然功能確定沒那麼豐富,只提供了一些簡單消息發送、接收的功能。Route
路由服務,主要用於客戶端鑑權、消息的轉發等;提供一些 http 接口,能夠用於查看系統狀態、在線人數等功能。固然服務端、路由均可以水平擴展。架構
這是一個消息發送的流程圖,假設如今部署了兩個服務端 A、B 和一個路由服務;其中 ClientA
和 ClientB
分別和服務端 A、B 保持了長鏈接。框架
當 ClientA
向 ClientB
發送一個 hello world
時,整個的消息流轉如圖所示:函數
http
將消息發送到 Route
服務。ClientB
是鏈接在 ServerB
上;因而再經過 http
將消息發送給 ServerB
。ServerB
將消息經過與 ClientB
的長鏈接通道 push
下去,至此消息發送成功。這裏我截取了 ClientA
向 Route
發起請求的代碼:
能夠看到這就是利用 okhttp
發起了一個 http
請求,這樣雖然能實現功能,但其實並不優雅。工具
舉個例子:假設咱們須要對接支付寶的接口,這裏發送一個 http 請求天然是沒問題;但對於支付寶內部各部門直接互相調用接口時那就不該該再使用原始的 http 請求了。編碼
應該是由服務提供方提供一個 api
包,服務消費者只須要依賴這個包就能夠實現接口調用。spa
固然最終使用的是 http、仍是自定義私有協議均可以。
也相似於咱們在使用 Dubbo
或者是 SpringCloud
時,一般是直接依賴一個 api
包,即可以像調用一個本地方法同樣調用遠程服務了,而且徹底屏蔽了底層細節,不論是使用的 http 仍是 其餘私有協議都不要緊,對於調用者來講徹底不關心。
這麼一說是否是有內味了,這不就是 RPC 的官方解釋嘛。
對應到這裏也是一樣的道理,Client
、Route
、Server
本質上都是一個系統,他們互相的接口調用也應當是走 RPC
才合理。
因此我重構以後的變成這樣了:
是否是代碼也簡潔了許多,就和調用本地方法同樣了,並且這樣也有幾個好處:
下面來聊聊具體是如何實現的。
其實在上文《動態代理的實際應用》 中也有講到,原理是相似的。
要想作到對調用者無感知,就得建立一個接口的代理對象;在這個代理對象中實現編碼、調用、解碼的過程。
對應到此處其實就是建立一個 routeApi
的代理對象,關鍵就是這段代碼:
RouteApi routeApi = new ProxyManager<>(RouteApi.class, routeUrl, okHttpClient).getInstance();
完整源碼以下:
其中的 getInstance()
函數就是返回了須要被代理的接口對象;而其中的 ProxyInvocation
則是一個實現了 InvocationHandler
接口的類,這套代碼就是利用 JDK
實現動態代理的三板斧。
查看 ProxyInvocation
的源碼會發現當咱們調用被代理接口的任意一個方法時,都會執行這裏的 invoke()
方法。
而 invoke()
方法天然就實現了上圖中提到的:編碼、遠程調用、解碼的過程;相信你們很容易看明白,因爲不是本次探討的重點就不過多介紹了。
其實理解這些就也就很容易看懂 Dubbo
這類 RPC
框架的核心源碼了,整體的思路也是相似的,只不過使用的私有協議,因此在編解碼時會有所不一樣。
因此你們要是想本身動手實現一個 RPC
框架,不妨參考這個思路試試,當用本身寫的代碼跑通一個 RPC
的 helloworld
時的感受是和本身整合了一個 Dubbo
、SpringCloud
這樣的第三方框架的感受是徹底不一樣的。
本文的全部源碼:
https://github.com/crossoverJie/cim
你的點贊與分享是對我最大的支持