在看此篇内容时须要浏览下面内容
从零开始学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