(五)dubbo源码解析---- dubbo @Reference consumer 远程调用过程

spring版本 5.x
jdk版本 1.8
dubbo版本 2.6.0

概述
    本文主要是通过阅读源代码的方式理解下 dubbo consumer 是如何调用远程服务的。

关键字
    动态代理, NIO

    通过第(四)篇文章我们知道@Reference 注解是通过动态代理实现的。如果对动态代理不是了解可以阅读下这篇博客:JAVA动态代理实现与原理详细分析。 这里不做动态代理的详细分析,只是简单概括下动态代理:

  1. 在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
  2. 通过 Proxy 类生成的代理对象与代理对象会实现同一个接口
  3. Proxy 类生成的代理对象会持有一个InvocationHandler
  4. 被代理的对象的所有方法都会通过InvocationHandler的 invoke()方法执行,所以想在代理阶段做任何事,只要修改 invoke方法就行了。
  5. Proxy 类生成的代理对象文件(类似常规的.class 文件)是存在内存中的

通过《@Reference 注解注入原理》这篇博客,我们已经知道被@Reference 修饰的域实际上是一个代理类。其实在 Debug 代码的时候,我们也会看到:
在这里插入图片描述
@Reference 注解修饰的域是通过InvokerInvocationHandler实现的动态代理。被代理类的任何方法都会通过通过InvokerInvocationHandler的 invoker 方法执行

/**
proxy 参数暂未用到,应该是代理类,与被代理类实现了同一接口
method 即被代理的方法
args 为被代理的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
        //这个 invoker是在解析@Reference 注解使创建的,通过Protocol.refer()。最后的远程调用也发生在这行代码里的 invoke方法。其中的 recreate()为重新构建返回结果。
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

继续往下跟踪,最后发现下一步的关键 invoke 发生在ProtocolFilterWrapper 中(buildInvokerChain方法),通过 dubbo的 filter 来继续 invoke(这也符合 dubbo 官方架构图中的描述:通过 cluster invoker调用 protocol filter, 再通过 protocol filter 调用 protocol invoker), 代码如下:

需要注意的是buildInvokerChain方法并不发生在远程调用阶段, 顾名思义这个方法其实是个构建调用链的方法,执行时机发生在构建 Invoker阶段,也就是Protocol的refer()方法,下面具体分析下代码:


private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        //这个 filter 列表加载的是通过 @Activate 注解修饰的group="consumer" 的dubbo  filter,
        //通过@Activate注解会获得三个 filter, 
        //分别是 ConsumerContextFilter 、FutureFilter、MonitorFilter, 
        //且这三个 filter 有序的,排序规则是按照@Activate注解的 order 字段。小的在前面,默认为零。 ConsumerContextFilter的 order 是-10000, 所以它排在这个列表的前面
        //这个 dubbo filter 列表会被依次调用执行
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
        	//此处为倒序, 因此 filter列表的头节点ConsumerContextFilter将赋值给 last 参数作为调用链的头节点返回。
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                //这里生成一个内部类,这个内部类有三个属性,也就这个作用域内的三个 final 类型的变量:
                //invoker
                //filter
                //next (也是一个 Invoker)
                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 {
                    //filter.invoke() 实际上执行的 next 的 invoke方法. 
                    //而 next 节点的 invoke 方法又是执行的 next 节点持有的 filter的invoke(),如此往复循环,完成链式调用
                        return filter.invoke(next, invocation);
                    }

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

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

    总结下invoke调用链的原理:每个节点是一个 Invoker对象,它会有三个成员变量,分别是 invoker、filter、next(Invoker类型), 每个Invoker对象的 invoke 方法里执行的是其成员变量filter的 invoke方法,而filter的 invoke方法执行的 next的 invoke 方法。 即: invoker.invoke() --> filter.invoke() --> next.invoke() --> filter.invoke()…

再调用过程中,在ListenerInvokerWrapper.invoke()打个断点,看下调用堆栈, 同样印证了我们上面所说的。
在这里插入图片描述

接下来分析如何进行的远程交互

    从上图得知,MonitorFilter所调用的 next invoker 是 DubboInvoker, 其本身没有 invoke 方法,调用的父类AbstractInvoker中的 invoke 方法, 而AbstractInvoker 中 invoke方法调用的是派生类的 doInvoke方法。最后我们回到 DubboInvoker类中的doInvoke()

 protected Result doInvoke(final Invocation invocation) throws Throwable {
        //忽略。。。
        ExchangeClient currentClient;
        if (clients.length == 1) {
            currentClient = clients[0];
        } else {
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
            int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
            if (isOneway) {
   //忽略。。。
            } else if (isAsync) {
   //忽略。。。
            } else {
                RpcContext.getContext().setFuture(null);
                //最后通过ExchangeClient执行远程调用
                return (Result) currentClient.request(inv, timeout).get();
            }
        } catch (TimeoutException e) {
     		// 忽略。。
        } 
    }

一路跟踪下去,最后发现发生在HeaderExchangeChannel的request方法中,如图:
在这里插入图片描述
最后发送请求的是dubbo 的 NettyClient,至此,我们终于印着了dubbo 是通过 NIO 进行远程交互的原理。至于具体如何进行远程通信,我们就得去阅读 netty 源代码了,本文就不再分析了。 本文还有很多细节没有分析到,如有错误欢迎读者指正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值