Dubbo源码解读-Consumer调用流程解析

上篇我们介绍了Consumer消费端服务列表刷新原理,地址如下

Dubbo源码解读-Consumer消费端服务列表刷新-CSDN博客

        本文主要针Dubbo消费端调用服务端流程,从dubbo源码角度进行解析。

        大家可以好好仔细读一下本文。有疑问欢迎留言。

        接着说明,读Dubbo源码最好是先对Spring源码有一定的了解。如果大家需要,我也可以针对Spring框架做一系列源码的解读专栏。

         不过不用担心,如果需要Spring的源码知识,文章中也会进行Spring源码铺垫介绍的。

        如果内容中有没描述清楚的,或者大家在阅读源代码有疑问的,欢迎留言,看到就会及时回复。

主要内容

消费端调服务端流程

        消费端依赖注入服务端代理对象后,调用过程整体细节是比较复杂的。因为好多同学为了应付面试或者觉得细节流程太繁琐或者复杂,只想弄清楚相对简练的过程。所以对细节流程做了一个流程概述的部分,方便大家理解大概过程,以及应对面试。更详细的过程可关注详细流程。

类的调用流程

ReferenceBeanInvocationHandler.invoker()->InvokerInvocationHandler.invoker()->MockClusterInvoker.invoke()->FailOverClusterInvoker.invoker()[或者是其他集群容错配置类]->DubboInvoker.doInvoke()->调用NettyClient发送消息和接收服务端响应

        其中NettyClient初始化以及调用流程会在后续文章详细介绍,都属于Netty的Api。

        本文不需要关注。调用流程只需要了解到DubboInvoker即可。

核心类:

  • MockClusterInvoker.invoke:消费端调用的入口逻辑。
  • FailOverClusterInvoker.invoke:默认集群容错算法。调用了负载均衡算法,选择invoker。实现集群容错功能
  • MockInvoker.invoke:实现Mock原理
  • RandomLoadBalance:默认负载均衡器。随即权重,实现负载均衡算法

Mock实现原理

        消费端以来注入的代理类,最终被代理的类为MockerClusterInvoker。实现Mock功能以及远程RPC调用功能。

  1. URL中获取Mock配置
  2. 如果没有mock信息,则走远程RPC调用
  3. 如果是force:则强制降级,调用本地方法(Mock原理,下面流程会详细叙述)
  4. 其他:则走RPC异常降级,先调用远程服务,如果发生RPC异常,则调用本地mock方法(2+3)

流程概述

        消费端依赖代理对象,代理调用的入口为MockClusterInvoker.invoke持有集群容错类Cluster,默认FailOverCluster。MockClusterInvoker实现了Mock调用的原理

概述总结:

  1. MockClusterInvoker.invoke
  2. 从Url上获取配置的mock信息
  3. 如果没有mock走远程RPC。
    1. 获取服务列表(订阅事件,服务列表本地缓存)
    2. 获取负载均衡算法。抽象类AbstractClusterInvoker
    3. 调用子类具体集群容错类的doInvoke(),实现远程服务调用。
      1. 调用负载均衡算法选择一个Invoker.
      2. FailOverClusterInvoker:根据retry循环重试
      3. FailBackClusterInvoker:调用失败,则返回空new RpcResult(),并记录失败请求,异步定时调用
      4. FailFastClusterInvoker:如果RpcExpection,则直接抛出,不重试。
      5. FailSafeClusterInvoker:如果RpcExpection,记录日志,返回空new RpcResult()
      6. ForkingClusterInvoker:同时调用多台主机,返回响应最快主机的结果。
      7. BroadcastClusterInvoker:服务列表挨个调用一边,场景:刷新本地缓存。
      8. AvailableClusterInvoker.
  4. 如果force是强制降级,则走本地mock.
  5. 其他。走RPC异常降级。即先远程,如果发生RPC异常,则走本地(3+4)

更进一步总结

  1. MockClusterInvoker.invoke
  2. 从Url上获取配置的mock信息
  3. 如果没有mock走远程RPC。
    1. 获取服务列表(订阅事件,服务列表本地缓存)
    2. 获取负载均衡算法。抽象类AbstractClusterInvoker
    3. 获取集群容错算法。
      1. 利用负载均衡算法,选择一个Invoker
      2. 调用Invoker,判断是否失败。失败则执行集群容错
  4. 如果force是强制降级,则走本地mock.
  5. 其他。走RPC异常降级。即先远程,如果发生RPC异常,则走本地(3+4)

详细流程

  1.  MockClusterInVoker.invoker():消费端调用核心流程
  2. 获取mock配置,是否有配置mock。
    1. directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY 
  3. 如果没有,直接RPC调用
    1. this.invoker.invoke(invocation);其中this.invoker指FailoverClusterInvokerr
    2. AbstractClusterInvoker.invoke()
    3. 获取服务列表List<Invoker>
      1. RegistryDiretory.List
      2. doList()
      3. 从缓存methodInvokerMap中,根据方法名称,获取对应的List<Invoker>
      4. 根据routers进行过滤
      5. 返回服务列表List<Invoker>
    4. SPI根据负载均衡算法:默认是random加全随机
    5. FailoverClusterInvoker.doInvoke(invocation, invokers, loadbalance)核心逻辑调用
      1. 获取重试次数配置
      2. 根据重试次数循环调用
      3. 每次重试都重新获取最新的服务列表
      4. 根据负载均衡算法选取一个服务
        1. invoker = select(loadbalance, invocation, copyinvokers, invoked);
          1. 此处的invoked是指RPC失败后,重试过的invoker,用于下次重试时,排除已经试过的节点
        2. sticky判断是否粘带连接,是则返回之前调用过的invoker
        3. doSelect()
        4. 如果invokers只有一个则直接返回。
        5. 调用具体负载均衡算法,选择一个服务 
          1. Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);
        6. 如果当前节点是已经试过的节点,则重新选一个invoker.
        7. 返回当前选中的invoker
      5. 记录已经调用过的invoker
      6. 调用Invoker: Result result = invoker.invoke(invocation);
        1. InvokerDelegate->Filter1-》Filter2-〉DubboInvoker
      7. DubboInvoker.doInvoker
        1. 单工通信:消费端注解配置。method:isReturn=false;
          1. 不需要返回值,减少一次服务端给客户端响应的TCP通信
          2. 不需要创建DefaultFeature
        2. 异步处理:
          1. netty本来就是异步通信,不等待返回结果。返回ResponseFuture设置到Rpc上下文。
          2. 需要返回结果时,调用ResponseFuture.get()阻塞等待即可
        3. 双端通信
          1. 等待服务端响应。服务端Netty通讯,有返回值的时候,服务端会回调Netty客户端的Read方法,会唤醒get等待的方法
          2. return (Result) currentClient.request(inv, timeout).get();
  4. 如果是force:则强制降级,调用本地方法(Mock原理)
    1. doMockInvoke():其实这里会从缓存服务列表methodInvokerMap取,不过取不到mock类型的Invoker.
    2. 创建MockInvoker对象
    3. MockInvoker.invoker()
    4. 获取mock配置,并处理
      1. return:截取return后内容,包装数据类型直接返回RpcResult
      2. throw:截取throw后内容,实例化异常类型,直接抛出
      3. 其他:调用具体的mock实现类中的方法进行降级。
        1. mock=true:消费端实现类名称为接口名称+Mock。
        2. 具体实现类。mock="com.ab.c.servcie":消费端实现类名称为mock中配置的借口
        3. 通过代理工厂javassisProxyFactrory将mock具体类专为Invoker,包装成abstractProxyInvoker.持有Wrapper,包装目标类。返回AbstractProxyInvoker
        4. 调用:AbstractProxyInvoker.invoker->doinvoker()->wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
        5. RPC或本地Mock方法的调用都会通过Invoker调用wrapper再调用被代理的方法。
  5. 其他则走RPC异常降级,先调用远程服务,如果发生RPC异常,则调用本地mock方法(2+3)

源码分析

 1.入口

        MockClusterInvoker.invoker()

public Result invoke(Invocation invocation) throws RpcException {
        Result result = null;

        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
        //如果没有mock,直接掉后端服务
        if (value.length() == 0 || value.equalsIgnoreCase("false")) {
            //no mock
            //this.invoker:持有的集群容错类,默认FailOverClusterInvoker
            result = this.invoker.invoke(invocation);
            //force是强制降级,不掉后端服务
        } else if (value.startsWith("force")) {
            //force:direct mock
            result = doMockInvoke(invocation, null);
        } else {
            //fail-mock
            //非强制降级,而是调用异常降级
            try {
                result = this.invoker.invoke(invocation);
            } catch (RpcException e) {
                if (e.isBiz()) {
                    throw e;
                } else {
                  
                    //降级逻辑
                    result = doMockInvoke(invocation, e);
                }
            }
        }
        return result;
    }

  2.远程调用

        2.1AbstractClusterInvoker.invoke 。本地缓存获取服务列表,选择负载均衡算法。

public Result invoke(final Invocation invocation) throws RpcException {
        checkWhetherDestroyed();
        LoadBalance loadbalance = null;
        //获取服务列表(从本地缓存中获取,本地服务列表来自于,消费端订阅,服务列表舒心事件)
        List<Invoker<T>> invokers = list(invocation);
        if (invokers != null && !invokers.isEmpty()) {
            //获取负载均衡算法
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                    .getMethodParameter(RpcUtils.getMethodName(invocation), Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
        }
        RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
        //调用核心逻辑
        return doInvoke(invocation, invokers, loadbalance);
    }

        2.2FailOverClusterInvoker.doInvoke:调用负载均衡算法,选择一个Invoker。如果调用失败,则根据集群容错算法进行重试。

public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        List<Invoker<T>> copyinvokers = invokers;
 
        String methodName = RpcUtils.getMethodName(invocation);
        //获取重试次数
        int len = getUrl().getMethodParameter(methodName, Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
        if (len <= 0) {
            len = 1;
        }
        // retry loop.
        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.
            //如果掉完一次后,服务列表更新了,再次获取服务列表
            if (i > 0) {
                checkWhetherDestroyed();
                copyinvokers = list(invocation);
                // check again
                checkInvokers(copyinvokers, invocation);
            }
            //根据负载均衡算法,选择一个服务调用
            Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
            //记录已经调用过的invoker
            invoked.add(invoker);
            RpcContext.getContext().setInvokers((List) invoked);
            try {
                //具体的服务调用逻辑
                Result result = invoker.invoke(invocation);
               
                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());
            }
        }
    }

2.3 AbstractClusterInvoker.select和doSelect 负载均衡选择invoker

protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
        if (invokers == null || invokers.isEmpty())
            return null;
        String methodName = invocation == null ? "" : invocation.getMethodName();

        boolean sticky = invokers.get(0).getUrl().getMethodParameter(methodName, Constants.CLUSTER_STICKY_KEY, Constants.DEFAULT_CLUSTER_STICKY);
        //粘带连接
        {
            //ignore overloaded method
            if (stickyInvoker != null && !invokers.contains(stickyInvoker)) {
                stickyInvoker = null;
            }
            //ignore concurrency problem
            if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))) {
                if (availablecheck && stickyInvoker.isAvailable()) {
                    return stickyInvoker;
                }
            }
        }
        //负载均衡核心逻辑
        Invoker<T> invoker = doSelect(loadbalance, invocation, invokers, selected);

        //如果是粘带服务,记录上一次调用的invoke个全局变量
        if (sticky) {
            stickyInvoker = invoker;
        }
        return invoker;
    }



private Invoker<T> doSelect(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
        if (invokers == null || invokers.isEmpty())
            return null;
        if (invokers.size() == 1)
            return invokers.get(0);
        if (loadbalance == null) {
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
        }
        //掉具体的负载均衡算法的select
        Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);

        //If the `invoker` is in the  `selected` or invoker is unavailable && availablecheck is true, reselect.
        //如果selected不为空,说明前一次调用失败了,这一个调用是重试调用
        //如果当前选择的invoker和前一次失败的invoker是同一个,则再选一次
        if ((selected != null && selected.contains(invoker))
                || (!invoker.isAvailable() && getUrl() != null && availablecheck)) {
            try {
                Invoker<T> rinvoker = reselect(loadbalance, invocation, invokers, selected, availablecheck);
                if (rinvoker != null) {
                    invoker = rinvoker;
                } else {
                    //Check the index of current selected invoker, if it's not the last one, choose the one at index+1.
                    int index = invokers.indexOf(invoker);
                    try {
                        //Avoid collision
                        invoker = index < invokers.size() - 1 ? invokers.get(index + 1) : invokers.get(0);
                    } 
                }
            } 
        }
        return invoker;
    }

         2.4 AbstractLoadBalance.select() 

public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        if (invokers == null || invokers.isEmpty())
            return null;
        if (invokers.size() == 1)
            return invokers.get(0);
        return doSelect(invokers, url, invocation);
    }

        2.5 默认RandomLoadBalance.doSelect()。默认随机权重算法。

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        int length = invokers.size(); // Number of invokers
        int totalWeight = 0; // The sum of weights
        boolean sameWeight = true; // Every invoker has the same weight?
        for (int i = 0; i < length; i++) {
            int weight = getWeight(invokers.get(i), invocation);
            totalWeight += weight; // Sum
            if (sameWeight && i > 0
                    && weight != getWeight(invokers.get(i - 1), invocation)) {
                sameWeight = false;
            }
        }
        if (totalWeight > 0 && !sameWeight) {
            // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
            int offset = random.nextInt(totalWeight);
            // Return a invoker based on the random value.
            for (int i = 0; i < length; i++) {
                //权重越大,随机数减去小于0的概率越大
                offset -= getWeight(invokers.get(i), invocation);
                if (offset < 0) {
                    return invokers.get(i);
                }
            }
        }
        //如果权重相同,则是真随机
        // If all invokers have the same weight value or totalWeight=0, return evenly.
        return invokers.get(random.nextInt(length));
    }

3.mock本地调用

        3.1 MockClusterInvoker.doMockInvoke

private Result doMockInvoke(Invocation invocation, RpcException e) {
        Result result = null;
        Invoker<T> minvoker;

        List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
        if (mockInvokers == null || mockInvokers.isEmpty()) {
            minvoker = (Invoker<T>) new MockInvoker(directory.getUrl());
        } else {
            minvoker = mockInvokers.get(0);
        }
        try {
            result = minvoker.invoke(invocation);
        } catch (RpcException me) {
            if (me.isBiz()) {
                result = new RpcResult(me.getCause());
            } else {
                throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());
            }
        } catch (Throwable me) {
            throw new RpcException(getMockExceptionMessage(e, me), me.getCause());
        }
        return result;
    }

        3.2 MockInvoker.invoke

        

public Result invoke(Invocation invocation) throws RpcException {
        String mock = getUrl().getParameter(invocation.getMethodName() + "." + Constants.MOCK_KEY);
        mock = normalizeMock(URL.decode(mock));
        //force:return jack
        if (mock.startsWith(Constants.RETURN_PREFIX)) {
            //截取return后面的内容
            mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();
            try {
                //获取方法的返回值类型
                Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
                //根据配置的mock值和返回值类型进行数据包装
                Object value = parseMockValue(mock, returnTypes);
                //不掉后端服务直接返回包装值
                return new RpcResult(value);
            } catch (Exception ew) {
                throw new RpcException("mock return invoke error. method :" + invocation.getMethodName()
                        + ", mock:" + mock + ", url: " + url, ew);
            }
            //force:throw com.xx.XXException
        } else if (mock.startsWith(Constants.THROW_PREFIX)) {
            mock = mock.substring(Constants.THROW_PREFIX.length()).trim();
            if (StringUtils.isBlank(mock)) {
                throw new RpcException("mocked exception for service degradation.");
            } else { // user customized class
                //实例化用户自定义的异常类
                Throwable t = getThrowable(mock);
                //异常往上抛
                throw new RpcException(RpcException.BIZ_EXCEPTION, t);
            }
        } else { //impl mock
            try {
                //调用具体的mock实现类
                Invoker<T> invoker = getInvoker(mock);
                //调用mock中的方法进行降级
                return invoker.invoke(invocation);
            } catch (Throwable t) {
                throw new RpcException("Failed to create mock implementation class " + mock, t);
            }
        }
    }

4.其他

       先远程调用,如果RPC失败,则本地调用。

总结:上面内容中,每个从业务流程和源码角度进行了详细分析,如果大家有疑问或者对文章排版任何方面有建议都可以留言评论,看到都会及时回复大家。

知识总结,分享不易,全文手敲,欢迎大家关注点赞评论收藏。

当然如果觉得文章写的不错,也可点击大赏按钮,深深的鼓励一下博主。哈哈!!!!

  • 107
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值