dubbo rpc

柔情痞子 提交于 2019-12-25 19:59:52

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

dubbo中rpc通信算是重头戏。本文基于dubbo-rpc-api dubbo-rpc-default两个包重点了解下dubbo rpc的实现原理。

#1、代码示例 既然要说原理,首先就是把实现了rpc的代码示例放在这里。

DemoService service = new DemoServiceImpl();
		protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange")));
		service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange")));
		assertEquals(service.getSize(new String[]{"", "", ""}), 3);

将其“肢解”,我们就能够对rpc有了一定的了解。 #1、生成实例 DemoService service = new DemoServiceImpl(); 我们知道平时我们在spring里面配置dubbo信息到spring,客户端在只有接口的情况下就能够调到实现类,就好像这行代码一样。你有没有问,客户端本来没这个类怎么就能调到实现类的呢?代理呗,您如是回答。那么我们继续。 #2、服务端发布自己的服务 ##2.1、生成代理

proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange"))

生成代理的过程就这么简单,而其中包含的东西真的不少,大致步骤: ###1、URL类会解析传递进来的字符串,根据规则将其放置到URL的各个字段里面去。 "dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange"是传递进来的字符串,仔细看它就是"dubbo://127.0.0.1:9020/DemoServie?codec=exchange". ###2、proxy.getInvoker(实例,接口,URL)做的事情比较多。 首先这个proxy是一个适配器,这个如果您跟代码会发现完全知道到,是因为这个适配器是在ExtendLoader里面动态生成的,在调用getInvoker方法的时候其实是在调用适配器里面的该方法。适配器里面的该方法会根据您传递进来的URL去寻找真正的实现类。这里找到类也许您会认为是JavassistProxyFactory,非也,其实找到的是StubProxyFactoryWrapper类,并把JavassistProxyFactory放置到该类中来。知道了这个类那么就该调用真正实现类的getInvoker方法。

  public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
        return proxyFactory.getInvoker(proxy, type, url);
    }

这里就直接调用了JavassistProxyFactory里面的getInvoker方法。

  public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper类不能正确处理带$的类名
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName, 
                                      Class<?>[] parameterTypes, 
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

但看这个方法貌似就做了两件事情:生成wrapper实例,返回一个AbstractProxyInvoker实例。这里面的wrapper真的是不能小觑,打开实现类会发现作者在这个类的代码量很大。那么他在做什么?

 ret = makeWrapper(c);

它在包装实例。将之前的DemoServiceImpl实例包装成了一个wrapper返回。这个过程做了三件事情 首先:拼接了三个方法串。

方法串1
 public void setPropertyValue(Object o, String n, Object v) {
        com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl w;
        try {
            w = ((com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl) $1);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl.");
    }


方法串2
public Object getPropertyValue(Object o, String n) {
        com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl w;
        try {
            w = ((com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl) $1);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        if ($2.equals("threadName")) {
            return ($w) w.getThreadName();
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl.");
    }

方法串3

  public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
        com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl w;
        try {
            w = ((com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl) $1);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        try {
            if ("invoke".equals($2) && $3.length == 2) {
                return ($w) w.invoke((java.lang.String) $4[0], (java.lang.String) $4[1]);
            }
            if ("get".equals($2) && $3.length == 1) {
                return ($w) w.get((com.alibaba.dubbo.rpc.protocol.dubbo.support.CustomArgument) $4[0]);
            }
            if ("timestamp".equals($2) && $3.length == 0) {
                return ($w) w.timestamp();
            }
            if ("keys".equals($2) && $3.length == 1) {
                return ($w) w.keys((java.util.Map) $4[0]);
            }
            if ("getSize".equals($2) && $3.length == 1 && $3[0].getName().equals("[Ljava.lang.String;")) {
                return ($w) w.getSize((java.lang.String[]) $4[0]);
            }
            if ("getSize".equals($2) && $3.length == 1 && $3[0].getName().equals("[Ljava.lang.Object;")) {
                return ($w) w.getSize((java.lang.Object[]) $4[0]);
            }
            if ("echo".equals($2) && $3.length == 1 && $3[0].getName().equals("java.util.Map")) {
                return ($w) w.echo((java.util.Map) $4[0]);
            }
            if ("echo".equals($2) && $3.length == 1 && $3[0].getName().equals("java.lang.String")) {
                return ($w) w.echo((java.lang.String) $4[0]);
            }
            if ("enumlength".equals($2) && $3.length == 1 && $3[0].getName().equals("[Lcom.alibaba.dubbo.rpc.protocol.dubbo.support.Type;")) {
                return ($w) w.enumlength((com.alibaba.dubbo.rpc.protocol.dubbo.support.Type[]) $4[0]);
            }
            if ("enumlength".equals($2) && $3.length == 1 && $3[0].getName().equals("com.alibaba.dubbo.rpc.protocol.dubbo.support.Type")) {
                return ($w) w.enumlength((com.alibaba.dubbo.rpc.protocol.dubbo.support.Type) $4[0]);
            }
            if ("stringLength".equals($2) && $3.length == 1) {
                return ($w) w.stringLength((java.lang.String) $4[0]);
            }
            if ("sayHello".equals($2) && $3.length == 1) {
                w.sayHello((java.lang.String) $4[0]);
                return null;
            }
            if ("nonSerializedParameter".equals($2) && $3.length == 1) {
                w.nonSerializedParameter((com.alibaba.dubbo.rpc.protocol.dubbo.support.NonSerialized) $4[0]);
                return null;
            }
            if ("returnNonSerialized".equals($2) && $3.length == 0) {
                return ($w) w.returnNonSerialized();
            }
            if ("getThreadName".equals($2) && $3.length == 0) {
                return ($w) w.getThreadName();
            }
            if ("getbyte".equals($2) && $3.length == 1) {
                return ($w) w.getbyte(((Byte) $4[0]).byteValue());
            }
            if ("gerPerson".equals($2) && $3.length == 1) {
                return ($w) w.gerPerson((com.alibaba.dubbo.rpc.protocol.dubbo.support.Person) $4[0]);
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + $2 + "\" in class com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl.");
    }

然后、生成实例

ClassGenerator cc = ClassGenerator.newInstance(cl); 在这个实例里面加属性和方法(包括上面拼接的三个方法串)。 最后,生成cc的实例。将其转为Wrapper类型返回。 至此,我们可以这样理解,这样动态生成的Wrapper已经不是普通的Wrapper了,虽然普通的Wrapper也有方法invokeMethod。但是它并没有关联实际的实现类,作者在这块做了一个动态的Wrapper,代理了普通的Wrapper。 继续,别蒙,至此我们得到了一个AbstractProxyInvoker实例。 至此proxy.getInvoker()交代完毕。 ##2.2、protocol.export(Invoker<T> invoker) throws RpcException; 上文我们说这个参数invoker实际的类型是AbstractProxyInvoker。 接下来我们看看这个方法做什么了 ####1、protocol本身是protocol的适配器,会根据传入进来的URL找到真正实现类的Wrapper。这里为什么生成的是实现类的包装器,请读者去ExtendLoader里面一探究竟。Protocol的包装器类有以下两个 class com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper class com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper 最终返回类型是ProtocolFilterWrapper


    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }
    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
            for (int i = filters.size() - 1; i >= 0; i --) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        return last;
    }

首先是创建一个过滤器链,将AbstractProxyInvoker实例层层包装 然后是调用protocol.export方法。 这个方法有是一个适配器代理找包装器的过程,最后发现没有包装器,直接返回了协议类DubboProtocol。该类的作用就是将Invoker转成Exporter,并调用dubbo根据这个url连接注册中心(或者直连)。 服务器端的内容就讲到这里。接下来我们关注客户端。 #3、客户端去获取服务 ##3.1、将请求的url包装成invoker protocol.refer(Class<T> type, URL url) 该方法返回ListenerInvokerWrapper类型的实例 ##3.2、获得invoker的代理。 proxy.getProxy(Invoker<T> invoker) throws RpcException proxy会根据ExtendLoader类获得这个类的wrapper:StubProxyFactoryWrapper。最终会调用该类的getProxy方法去获得代理。 1、调用具体的代理工厂创建一个invoker的代理类。这里面是JavassistProxyFactory类生成的代理。

  public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

##3.3、调用接口方法 service.getSize(null)。 调用接口方法就是去执行上面代理类的过程,具体过程大体是: ###1、进入InvokerInvocationHandler的invoke方法 ###2、invoker方法发现我们调用的不是简单的方法(hashCode,equals)等,那么进入invoker。

invoker.invoke(new RpcInvocation(method, args)).recreate()

####2.1、包装RpcInvocation类的实例。 ####2.2、调用ProtocolFilterWrapper类的invoke方法。 ####2.3、沿着过滤器链执行,知道执行真正的实现类invoker ####2.4、真正的invoker是DubboInvoker,它的doInvoke方法dubbo去找真正实现类去执行。这里面dubbo支持了同步和异步。

至此,调用端的逻辑交代完毕。

dubbo rpc的东西当然不止这些,其他别的东西还会陆续介绍。本文关注的是rpc 的主线,对代码的执行逻辑做一个交代。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!