netty實戰--手寫rpc框架

在看此篇內容時須要瀏覽下面內容
從零開始學netty——如何面對粘包和拆包
從零開始學netty——自定義協議java

rpc簡介

rpc你們大概都據說過,遠程過程調用。簡單來講,就是個人一個操做是遠程操做的給的結果,舉個例子,考試做弊,你把考題發出去了,你同窗幫你作好把答案傳輸給你,而後你就把答案寫上,那麼在判卷老師眼裏,你回答的還不錯,可是,其實你是作了遠程過程調用的。git

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

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

藉助netty手寫rpc

rpc的要點dom

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

消息傳給遠程

這裏就是創建鏈接的過程,代碼都比較套路,這裏不列舉。最後會貼出代碼地址的,這裏先通思路。序列化沒有選擇pb,而是選擇了protostuff,這裏得解釋一下緣由。異步

rpc傳遞的是什麼

既然是方法調用,一個方法的惟一標誌是類名,方法名,參數類型。你還得把方法參數也傳遞走。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異步如何準確的返回結果

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在調用的時候更簡單,通信部分纔是rpc的主要點,經過反射等方式,讓遠程的機器進行運算,而且返回結果。全部的代碼以下:https://github.com/xpbob/lightrpc

相關文章
相關標籤/搜索