dubbo服务调用

221 阅读3分钟

1. 拿到InvokeInvocationHandle后,调用invoke()方法

2.invoke方法中如果是object类的方法,则执行,然后调用MockClusterInvoker的invoke()方法

MockClusterInvoker中包含了

  • 服务目录Directory--》RegistryDirectory
  • 下一个invoker-->FailOverClusterInvoker
  • 将方法名和参数封装成RpcInvocation

3.进入MockClusterInvoker(这个类的作用:进行服务降级)的Invoke()方法中

  • 获得"mock"配置项,有多种配置方式
  • 无mock,则调用下一个invoker的invoke()方法
  • 如果mock值是以force开头,直接调用Mock Invoker执行本地mock逻辑
  • 调用原Invoker,发起RPC调用,如果是业务异常则直接抛出,如果是非业务异常,则执行fail-mock逻辑,调用Mock Invoker执行本地逻辑

4.下一个Invoker是FailOverClusterInvoker(此类的作用:服务容错)失败重试

  • 首先进入父类AbstractClusterInvoker的invoke方法中,模板方法
  • 获得LoadBalance对象

4.1 怎么获得负载均衡对象呢?

  • list(invocation)在服务RegistryDirectory中获得所有服务提供者Invoker集合

4.1.1 怎么在服务目录中服务提供者的所有Invoker集合呢?

  • list方法在父类AbstractDirectory中,模板方法,然后doList(invocation)调用具体的策略(RegistryDirectory---doList()方法)
4.1.1.1 进入RegistryDirectory(里面包含url和所有Invoker的集合,方法和所有Invoker的两个map)的doList方法

  • 拿到methodInvokerMap,这个map是方法和Invoker的映射, 首先根据:方法名.第一个参数获取所有Invoker, 如果为空,则使用方法名从map中获取, 如果为空,获取*所有的Invoker集合, 如果为空,则循环这个map集合,拿到第一个,返回 这样就获取到了所有的Invoker集合,返回上一步
4.1.1.2 根据服务目录获取到了所有的Invoker集合,接下来进行Route.route(List invokers)路由过滤

如果配置了条件路由ConditionRoute会进入ConditionRoute.route方法,进行正则匹配,得到剩下的Invoker集合 这样就获取到了过滤后的Invoker集合

4.1.2 用spi的方式,从方法上获取负载均衡策略,默认是随机

  • 设置调用编号,若是异步调用,将名为id,值为原子自增的值,设置到invocation的attachment中
  • 执行调用doInvoker(invocation,invokers,loadBalance)

5.进入FailOverClusterInvoker的doInvoker中

 public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        List<Invoker<T>> copyinvokers = invokers;
        //检查copyinvokers即可用Invoker集合是否为空,如果为空,那么抛出异常
        checkInvokers(copyinvokers, invocation);
        //得到最大可调用次数:最大可重试次数+1,默认最大可重试次数Constants.DEFAULT_RETRIES = 2
        int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
        if (len <= 0) {
            len = 1;
        }
        // retry loop.
        //保存最后一次调用的异常
        RpcException le = null; // last exception.
        //保存已经调用过的Invoker
        List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size()); // invoked invokers.
        Set<String> providers = new HashSet<String>(len);
        //failover机制核心实现:如果出现调用失败,那么重试其他服务器
        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.
            // 重试时,进行重新选择,避免重试时invoker列表已发生变化.
            // 注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变
            if (i > 0) {
                checkWhetherDestroyed();
                //根据Invocation调用信息从Directory中获取所有可用Invoker
                copyinvokers = list(invocation);
                // check again
                //重新检查一下
                checkInvokers(copyinvokers, invocation);
            }
            // 根据负载均衡机制从copyinvokers中选择一个Invoker
            Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
            // 保存每次调用的Invoker
            invoked.add(invoker);
            // 设置已经调用的 Invoker 集合,到 Context 中
            RpcContext.getContext().setInvokers((List) invoked);
            try {
                // RPC 调用得到 Result
                Result result = invoker.invoke(invocation);
                // 重试过程中,将最后一次调用的异常信息以 warn 级别日志输出
                if (le != null && logger.isWarnEnabled()) {
                    logger.warn("Although retry the method " + invocation.getMethodName()
                            + " 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) {
                // 其他性质的异常统一封装成RpcException
                le = new RpcException(e.getMessage(), e);
            } finally {
                providers.add(invoker.getUrl().getAddress());
            }
        }
        // 最大可调用次数用完还得到Result的话,抛出RpcException异常:重试了N次还是失败,并输出最后一次异常信息
        throw new RpcException(le != null ? le.getCode() : 0, "Failed to invoke the method "
                + invocation.getMethodName() + " 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 != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le);
    }