在看此篇內容時須要瀏覽下面內容
從零開始學netty——如何面對粘包和拆包
從零開始學netty——自定義協議java
rpc你們大概都據說過,遠程過程調用。簡單來講,就是個人一個操做是遠程操做的給的結果,舉個例子,考試做弊,你把考題發出去了,你同窗幫你作好把答案傳輸給你,而後你就把答案寫上,那麼在判卷老師眼裏,你回答的還不錯,可是,其實你是作了遠程過程調用的。git
rpc的好處也在上面的例子中體現了,當自身能力不行的時候,能夠依靠強大的遠程力量來作到結果。若是本身有能力回答卷子,那就不必走rpc了。換句話說就是github
自身執行的消耗 > 別人執行的消耗+傳輸的消耗spring
rpc的要點dom
這裏就是創建鏈接的過程,代碼都比較套路,這裏不列舉。最後會貼出代碼地址的,這裏先通思路。序列化沒有選擇pb,而是選擇了protostuff,這裏得解釋一下緣由。異步
既然是方法調用,一個方法的惟一標誌是類名,方法名,參數類型。你還得把方法參數也傳遞走。ide
private String id; private String className; private String methodName; private Object[] args; private Class<?>[] parameterTypes;
這裏還有傳輸一個id,是爲了作標誌,例如我發了題目出去,最但願的就是回到個人是第4題答案是什麼,而不是xxxx問題的答案是什麼。id就是惟一表示一次問題的。.net
你們也發現了裏面有Object類型和Class類型,這些類型是pb裏沒有的,因此此時pb就不適合做爲序列化的選擇了。線程
收到的就比較簡單了,就是惟一的id以及結果代理
private String id; private Object result;
netty的返回的結果是在handler裏,而不是咱們的業務線程,如何傳遞就成爲了一個問題。上面的惟一的id就是解決的關鍵點,我選擇了SynchronousQueue來做爲傳遞的媒介,若是不瞭解這個類的能夠先查看一下,他主要就是做爲傳遞媒介的,有點相似阻塞隊列。
private static ConcurrentHashMap<String, SynchronousQueue> mapInfo = new ConcurrentHashMap<>();
使用一個map來保存id,和傳遞媒介,業務線程只要拿着SynchronousQueue就好,等消息收到,就把結果放入SynchronousQueue中,業務線程就能夠拿到結果了,與此同時,要把id從map裏移除。
想作到方法調用,還擴充了部分功能,這個是裝飾者或者代理模式的效果。由於這裏只作一層包裝,因此選擇代理模式,若是是不斷的擴充功能的狀況,裝飾者會更好一些。由於咱們是java編寫,動態代理就是一個不錯的選擇。
public static <T> T getProxy(final Class<T> clazz) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, new InvocationHandler() { @Override public 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的主要點,經過反射等方式,讓遠程的機器進行運算,而且返回結果。全部的代碼以下:https://github.com/xpbob/lightrpc