rpc框架簡易實現

dubbo,作爲一個成熟、易用、多支持的遠程方法調用框架,應用廣泛。爲了透徹理解dubbo,我們今天來實現一把自己的dubbo。

分析

爲了實現dubbo,我們需要做哪些事情呢?

  1. 遠程,必然涉及到網絡通信,這個我們用成熟的netty框架來做
  2. 方法調用轉爲網絡請求,客戶端的方法調用,可以通過aop將行爲轉爲網絡請求。
    那麼,網絡請求要做哪些事情呢?要調用遠程方法,這就意味着服務端需要知道調用的類、方法以及方法參數值。
    所以我們封裝了一個遠程調用信息類來進行交互:RpcInfo(還有表示返回結果的result、success、exception等屬性),在進行網絡通信時,數據通過Kryo的方式序列化(因爲它佔用空間少、速度快,但是它要求序列化的類必須有無參的構造方法)
  3. 方法調用與響應結果反饋是不同的線程,如何拿到返回結果數據。
    這裏需要一個通訊機制,本項目採用簡單的方式,應用中維護一個map,在發送前,生成id作爲key,然後將調用信息對象放入到map中,接着通過該調用信息對象wait,阻塞。在調用結果返回時,調用的線程就會被喚醒,根據最新的返回結果來給方法調用者反饋。

實現步驟

1、netty框架

首先客戶端與服務端的netty使用代碼
服務端:
在這裏插入圖片描述

客戶端:
在這裏插入圖片描述

2、觸發網絡請求

方法調用轉化爲網絡請求,此處不做aop實現,採用直接調用的形式(也就是aop攔截器中會做的事情)
在這裏插入圖片描述

3、錯誤信息反饋

如果從invoke開始直到拿到結果的過程中出錯,怎麼提示給客戶端?我們將錯誤信息封裝到RpcInfo中,在出現異常的時候,就將該對象寫入到結果中。
在這裏插入圖片描述

4、異常信息的唯一請求id

但是netty的exceptionCaught方法是沒有數據信息參數的,在返回異常信息的時候標示客戶端請求的唯一id拿不到,怎麼辦?

很容易想到的一種方案是在服務端讀到id信息之後就寫入threadLocal中,之後exceptionCaught方法中從threadLocal取就好,筆者一開始也是這麼想的,但是後來發現netty的handler鏈路不一定是在一個線程中:
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

所以需要加上對同一個請求handler鏈路跨越多個線程的支持,筆者的實現方案是在channelRead,write前後都加上try catch,這樣就不會走到exceptionCaught中。

想到rpc實現比如dubbo可以增加許多用戶自定義filter,也爲了方便通過handler給本rpc實現添加更多的特徵,將這部分代碼進行了抽取,提取出一個ChannelHandlerAdapter基類,本實現中用到的ChannelHandlerAdapter都繼承該基類(讀取在反序列化之前,寫操作在序列化之後的除外)。
在這裏插入圖片描述

在這裏插入圖片描述

5、客戶端與服務端handler

接下來的就比較好理解了
客戶端的handler
在這裏插入圖片描述

服務端端handler
在這裏插入圖片描述

6、序列化的handler

在這裏插入圖片描述

代碼詳見:https://github.com/guzhangyu/practice-Code/tree/master/src/main/java/com/phei/netty/rpc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章