netty實戰——手寫rpc框架

rpc簡介

rpc大家大概都聽說過,遠程過程調用。簡單來說,就是我的一個操作是遠程操作的給的結果,舉個例子,考試作弊,你把考題發出去了,你同學幫你做好把答案傳輸給你,然後你就把答案寫上,那麼在判卷老師眼裡,你回答的還不錯,但是,其實你是做了遠程過程調用的。

rpc的好處也在上面的例子中體現了,當自身能力不行的時候,可以依靠強大的遠程力量來做到結果。如果自己有能力回答卷子,那就沒必要走rpc了。換句話說就是

自身執行的消耗 > 別人執行的消耗+傳輸的消耗

藉助netty手寫rpc

rpc的要點

  1. 消息傳給遠程
  2. 看起來和本地調用差不多
  3. 服務註冊

消息傳給遠程

這裡就是建立連接的過程,代碼都比較套路,這裡不列舉。最後會貼出代碼地址的,這裡先通思路。序列化沒有選擇pb,而是選擇了protostuff,這裡得解釋一下原因。

rpc傳遞的是什麼

既然是方法調用,一個方法的唯一標誌是類名,方法名,參數類型。你還得把方法參數也傳遞走。

private String id;private String className;private String methodName;private Object[] args;private Class>[] parameterTypes; 

這裡還有傳輸一個id,是為了做標誌,例如我發了題目出去,最希望的就是回到我的是第4題答案是什麼,而不是xxxx問題的答案是什麼。id就是唯一表示一次問題的。

大家也發現了裡面有Object類型和Class類型,這些類型是pb裡沒有的,所以此時pb就不適合作為序列化的選擇了。

收到的就比較簡單了,就是唯一的id以及結果

private String id;private Object result;

netty異步如何準確的返回結果

netty的返回的結果是在handler裡,而不是我們的業務線程,如何傳遞就成為了一個問題。上面的唯一的id就是解決的關鍵點,我選擇了SynchronousQueue來作為傳遞的媒介,如果不瞭解這個類的可以先查看一下,他主要就是作為傳遞媒介的,有點類似阻塞隊列。

private static ConcurrentHashMap mapInfo = new ConcurrentHashMap<>();

使用一個map來保存id,和傳遞媒介,業務線程只要拿著SynchronousQueue就好,等消息收到,就把結果放入SynchronousQueue中,業務線程就可以拿到結果了,與此同時,要把id從map裡移除。

看起來和本地調用差不多

想做到方法調用,還擴充了部分功能,這個是裝飾者或者代理模式的效果。因為這裡只做一層包裝,所以選擇代理模式,如果是不斷的擴充功能的情況,裝飾者會更好一些。因為我們是java編寫,動態代理就是一個不錯的選擇。

public static  T getProxy(final Class clazz) {return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {RpcRequest request = new RpcRequest();request.setMethodName(method.getName());request.setClassName(clazz.getName());Class>[] parameterTypes = method.getParameterTypes();request.setArgs(args);String id = UUID.randomUUID().toString();request.setId(id);SynchronousQueue queue = new SynchronousQueue();ResultInfo.putSunchronousQuee(id, queue);Client.write(request);return queue.take();}});}

服務註冊

這裡使用了比較簡單的方法,就是手動把服務加入。

public static void put(Object value) {Class>[] interfaces = value.getClass().getInterfaces();for(Class> interfaceTmp:interfaces){services.put(interfaceTmp.getName(), value);}}

這裡選擇把接口作為key,對象作為value。如果結合spring就可以更簡單一些,通過註解來做,不用自己手動寫了。

rpc實現總結

這裡實現的rpc的基礎功能,就是遠程調用。技術點就在動態代理和消息通訊上。動態代理的目的是為了讓rpc在調用的時候更簡單,通訊部分才是rpc的主要點,通過反射等方式,讓遠程的機器進行運算,並且返回結果。所有的代碼如下:github.com/xpbob/lightrpc


分享到:


相關文章: