Dubbo进阶(十一)- Dubbo 请求调用过程(一)

上一篇文章主要分析了消费者通过 获取代理对象的详细过程,以及对象内部原理,本文将从具体调用出发,一步一步深入Dubbo 调用内部细节。

前期铺垫

博主这些文章对Dubbo 分析中,都是以API 调用为例子进行,而本文分析消费者调用逻辑中,仍然是这样,下面是相应代码:

    public static void main(String[] args) {
        ReferenceConfig<HelloService> reference = new ReferenceConfig<>();
        reference.setApplication(new ApplicationConfig("dubbo-consumer"));
        reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
        reference.setInterface(HelloService.class);
        HelloService service = reference.get();
        String message = service.hello("dubbo I am anla7856");
        System.out.println(message);
    }

而上篇文章中,详细的分析了 reference.get() 返回代理对象的构成:
在这里插入图片描述

InvokerInvocationHandler

在代理对象中,执行 hello 方法则是执行 proxy0.hello,即执行 InvokerInvocationHandlerinvoke 方法:

    @Override
    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);
        }
        // 判断是否为toString
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        // 判断是否为hashCode
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        // 判断是否为equals
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
		// 都不是则往下执行
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

以上方法依次判断了以下几个条件:

  1. 该方法对应的类如果是Object 类型,则直接执行该方法。
  2. 判断是否为toStringhashCodeequals,是则直接执行invoker 的对应方法
  3. 构造一个 RpcInvocation,用于执行invoker 的 invoker 方法,而RpcInvocation 的生层过程就是填充一些比如methodNameparameterTypesargumentsattachmentsinvoker 等信息。此invokerMockClusterInvoker

为啥此 invokerMockClusterInvoker,可以看这篇:
Dubbo 消费者中 代理对象 初始化详解

MockClusterInvoker

而后则会执行 MockClusterInvokerinvoker 方法:

    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        Result result = null;
		// 判断是否配置了 mock
        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
        if (value.length() == 0 || value.equalsIgnoreCase("false")) {
            // 没有mock 则直接往下调用
            result = this.invoker.invoke(invocation);
        } else if (value.startsWith("force")) {
            // 有强制 mock配置,所以直接调用本地mock实现。
            if (logger.isWarnEnabled()) {
                logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
            }
            result = doMockInvoke(invocation, null);
        } else {
            //当远程调用失败才调用的mock,即一种熔断兜底的功能
            try {
                result = this.invoker.invoke(invocation);
            } catch (RpcException e) {
                if (e.isBiz()) {
                    throw e;
                }

                if (logger.isWarnEnabled()) {
                    logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
                }
                result = doMockInvoke(invocation, e);
            }
        }
        return result;
    }

上面 MockClusterInvoker 中的invoker 方法主要是对配置方法中mock 进行 区分性判断。

  1. 当没有配置mock,则直接往下调用 服务提供者暴露接口。
  2. 有配置强制mock 的化,那么不进行远端调用,而直接调用本地mock实现
  3. 有mock,则当调用服务提供者失败时候,会使用本地mock实现兜底

本文将以无mock配置往下继续分析

AbstractClusterInvoker

往后将会执行到AbstractClusterInvoker 的 invoke方法,即在上文代码第一个if 就进入。

    public Result invoke(final Invocation invocation) throws RpcException {
    	// 判断是否destroy
        checkWhetherDestroyed();

        // 从RpcContext中,查询当前请求有没有携带其他信息
        Map<String, String> contextAttachments = RpcContext.getContext().getAttachments();
        if (contextAttachments != null && contextAttachments.size() != 0) {
        // 有则加入进去
            ((RpcInvocation) invocation).addAttachments(contextAttachments);
        }
		// 通过Directory 获取到所有可用的invoker
        List<Invoker<T>> invokers = list(invocation);
        // 初始化LoadBalance 的SPI ,默认使用的是 RandomLoadBanlance
        LoadBalance loadbalance = initLoadBalance(invokers, invocation);
        // 如果是异步调用,则需要 在Invocation 中增加一个id,用于标识这次Invocation
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        // 执行子类doInvoke方法
        return doInvoke(invocation, invokers, loadbalance);
    }

AbstractClusterInvoker 中,主要是做一些准备性工作:

  1. 判断是否被销毁
  2. 判断在 RpcContext.getContext() 是否有 attachments,有则需要加入到invocation中
  3. Directory 中 获取 所有 Invoker,初始化LoadBalance(负载均衡)
  4. 如果指明是异步调用,则需要记录下该次id做相应处理
  5. 执行子类的 doInvoker 方法

FailoverClusterInvoker

FailoverClusterInvoker 是一种轮询的集群策略,即当一个调用失败时,会去请求另一个,Dubbo 中默认使用的就是这种集群策略。
现在看看其doInvoke 方法:

    public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
       List<Invoker<T>> copyInvokers = invokers;
       // 检查invokers,即检查是否有可用的invokers
       checkInvokers(copyInvokers, invocation);
       // 获取方法名
       String methodName = RpcUtils.getMethodName(invocation);
       // 获取重试次数,默认有三次机会,最少有一次。
       int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
       if (len <= 0) {
           len = 1;
       }
       // le 为记录最后一次错误。
       RpcException le = null; // last exception.
       List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
       Set<String> providers = new HashSet<String>(len);
       // 循环重试
       for (int i = 0; i < len; i++) {
           //Reselect before retry to avoid a change of candidate `invokers`.
           //NOTE: if `invokers` changed, then `invoked` also lose accuracy.
           // 为了保证准确性,除第一次外,每次循环,都需要重新检查下invokers的活性
           // 一旦某个invoker失活,会被检测到。
           if (i > 0) {
               checkWhetherDestroyed();
               copyInvokers = list(invocation);
               // check again
               checkInvokers(copyInvokers, invocation);
           }
           // 调用负载均衡,选择出某一个invokers
           Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
           // 将选出的invoker加入,并存储到上下文中。
           invoked.add(invoker);
           RpcContext.getContext().setInvokers((List) invoked);
           try {
           // 使用选出的invoker,执行invoke方法。
               Result result = invoker.invoke(invocation);
               if (le != null && logger.isWarnEnabled()) {
                   logger.warn("Although retry the method " + methodName
                           + " in the service " + getInterface().getName()
                           + " was successful by the provider " + invoker.getUrl().getAddress()
                           + ", but there have been failed providers " + providers
                           + " (" + providers.size() + "/" + copyInvokers.size()
                           + ") from the registry " + directory.getUrl().getAddress()
                           + " on the consumer " + NetUtils.getLocalHost()
                           + " using the dubbo version " + Version.getVersion() + ". Last error is: "
                           + le.getMessage(), le);
               }
               return result;
           } catch (RpcException e) {
               if (e.isBiz()) { // biz exception.
                   throw e;
               }
               le = e;
           } catch (Throwable e) {
               le = new RpcException(e.getMessage(), e);
           } finally {
               providers.add(invoker.getUrl().getAddress());
           }
       }
       throw new RpcException(le.getCode(), "Failed to invoke the method "
               + methodName + " in the service " + getInterface().getName()
               + ". Tried " + len + " times of the providers " + providers
               + " (" + providers.size() + "/" + copyInvokers.size()
               + ") from the registry " + directory.getUrl().getAddress()
               + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
               + Version.getVersion() + ". Last error is: "
               + le.getMessage(), le.getCause() != null ? le.getCause() : le);
   }

上面代码比较长,总结起来有以下几步:

  1. 检查 传入的 invokers 活性,并根据其确定重试次数
  2. 第二次及以后循环,都需要重新检查invokers 的最新活性,从而保证精准性
  3. 执行负载均衡逻辑选出invoker,并保存到上下文
  4. 使用选出的 invoker,执行其invoke方法,并获取返回结果。

invoker.invoke(invocation)

整个就只有这一句了,使用 invoker去执行,包括超时,异步,网络通信都在里面,下面仔细分析。

  1. InvokerWrapperinvoke 方法
    首先进入的是InvokerWrapper 的invoke方法,里面则仅仅包装一层,是直接执行invoker.invoke 方法。
    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        return invoker.invoke(invocation);
    }

下面结合 invoker 图来分析。
在这里插入图片描述
2. ProtocolFilterWrapperinvoke 方法:

        @Override
        public Result invoke(Invocation invocation) throws RpcException {
        // 异步使用filterInvoker 执行invoke
            Result asyncResult = filterInvoker.invoke(invocation);

            asyncResult.thenApplyWithContext(r -> {
                for (int i = filters.size() - 1; i >= 0; i--) {
                    Filter filter = filters.get(i);
                    // onResponse callback
                    if (filter instanceof ListenableFilter) {
                        Filter.Listener listener = ((ListenableFilter) filter).listener();
                        if (listener != null) {
                            listener.onResponse(r, filterInvoker, invocation);
                        }
                    } else {
                        filter.onResponse(r, filterInvoker, invocation);
                    }
                }
                return r;
            });

            return asyncResult;
        }

ProtocolFilterWrapper 的invoke方法其实逻辑比较复杂,Result asyncResult = filterInvoker.invoke(invocation); 里面执行了ProtocolFilterWraper 中所有相关链式的 Filter
主要有哪些Filter呢?又是如何初始化的呢?

ProtocolFilterWrapper 中 Filter 构建

ProtocolFilterWrapper 执行 refer 初始化时,会执行 buildInvokerChain 方法,而 ProtocolFilterWrapperFilter 就是在这里面构建的:

    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        // 获取所有符合要求的Filter
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

        if (!filters.isEmpty()) {
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

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

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

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

                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        Result asyncResult;
                        try {
                            asyncResult = filter.invoke(next, invocation);
                        } catch (Exception e) {
                            // onError callback
                            if (filter instanceof ListenableFilter) {
                                Filter.Listener listener = ((ListenableFilter) filter).listener();
                                if (listener != null) {
                                    listener.onError(e, invoker, invocation);
                                }
                            }
                            throw e;
                        }
                        return asyncResult;
                    }

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

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

        return new CallbackRegistrationInvoker<>(last, filters);
    }

初始化逻辑也简单,主要关心下有哪些Filter 被选中,并且组件顺序(类加载本就无法保证顺序,但是可以通过order 参数排序),而一大块实现,则只是执行Filterinvoke方法

在 dubbo filter中,有以下一些SPI 类:

echo=org.apache.dubbo.rpc.filter.EchoFilter    # provider  -110000
generic=org.apache.dubbo.rpc.filter.GenericFilter  # provider  -20000
genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter  # consumer  20000
token=org.apache.dubbo.rpc.filter.TokenFilter
accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter # provider 
activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter  # consumer 
classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter  # provider -30000
context=org.apache.dubbo.rpc.filter.ContextFilter  # provider -10000
consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter   # consumer -10000
exception=org.apache.dubbo.rpc.filter.ExceptionFilter   # provider
executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter  # provider
deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter   # consumer 
compatible=org.apache.dubbo.rpc.filter.CompatibleFilter  # provider,consumer
timeout=org.apache.dubbo.rpc.filter.TimeoutFilter   # provider
...

在这里插入图片描述
上述Filter 中,选中有两个条件:

  1. @Activate 修饰
  2. url中有指定,或者@Activate 中 value 为空的
  3. group 这边,ProtocolFilterWrapper 主要体现为 consumer
  4. 如果有传入 names,则需要对names进行进一层筛选,此处如果是加入该name对应的spi,如果是default类型,则需要将该SPI 放到最前面。

最终,有三个Filter 脱颖而出,顺序依次是:ConsumerContextFilterMonitorFilterFutureFilter(排序顺序,order 越小月往前)

下文

如今已经知道了ProtocolFilterWrapper 里面 filter 逻辑了,下一篇文章则继续验证 ProtocolFilterWrapper 的filter 调用逻辑走。

觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,Dubbo小吃街不迷路:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值