dubbo cluster--调用

上一篇订阅介绍了服务端如何将provider暴露给客户端。

暴露完成后,会在客户端缓存,也就是在RegistryDirectory下的methodLocalMap下。

客户端经过负载均衡和访问拦截后访问远程provider,然后返回结果给调用方。下图是客户端调用过程的序列图。
dubbo cluster--调用_第1张图片

在dubbo spi核心概念我们介绍过Filter和InvokerListener是如何通过wrapper作为扩展点织入进dubbo的。通过Protocol#refer返回给客户端的invoker其实是
Cluster invoker
–filter invoker
   –InvokerListener
      –Invoker
这样的结构,所以在调用时会通过Cluster往下依次调用,而Cluster则会经过select和loadBalance等过程。

术语

对Reference,Cluster invoker等陌生的可以参考dubbo cluster架构篇

备援,调用失败时,会切换到另一个节点调用。备援一般会有次数限制,超过固定次数后则判定为调用失败。

负载均衡,将请求均衡的分布到集群中的每个节点上。Dubbo支持一致性hash,轮询,随机以及最小连接等策略,默认是随机。

负载均衡和备援合起来组成了dubbo的HA方案。

invoke过程

客户端的调用由Proxy委派给Cluster,以下的代码以dubbo默认策略也就是备援策略– FailoverClusterInvoker,其继承了AbstractClusterInvoker

AbstractClusterInvoker#invoke,只提取关键代码

public Result invoke(final Invocation invocation) throws RpcException {
        List<Invoker<T>> invokers = list(invocation);
        return doInvoke(invocation, invokers, loadbalance);
    }

protected  List<Invoker<T>> list(Invocation invocation) throws RpcException {
        List<Invoker<T>> invokers = directory.list(invocation);
        return invokers;
    }

RegistryDirectory#list,从methodInvokerMap里对应方法的Invoker.
1. 方法的第一个参数可以作为动态路由参数
2. 方法名没有对应Invoker时,就取服务通用的
3. 以上几种情况都没有对应Invoker的情况下,返回第一组

public List<Invoker<T>> list(Invocation invocation) throws RpcException {

        List<Invoker<T>> invokers = doList(invocation);
        return invokers;
    }

public List<Invoker<T>> doList(Invocation invocation) {
        List<Invoker<T>> invokers = null;
        Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // local reference
        if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
            String methodName = invocation.getMethodName();
            Object[] args = invocation.getArguments();
            if(//第一个参数是String或者Enum) {
                invokers = localMethodInvokerMap.get(methodName + "." + args[0]); // 可根据第一个参数枚举路由
            }
            if(invokers == null) {
                invokers = localMethodInvokerMap.get(methodName);
            }
            if(invokers == null) {
                invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
            }
            if(invokers == null) {
                Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator();
                if (iterator.hasNext()) {
                    invokers = iterator.next();
                }
            }
        }
        return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
    }

FailoverClusterInvoke#doInvoke,提取这个方法的关键逻辑。

public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        List<Invoker<T>> copyinvokers = invokers;

        int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
        if (len <= 0) {
            len = 1;
        }

        // retry loop.
        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++) {
            //重试时,进行重新选择,避免重试时invoker列表已发生变化.
            //注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变
            if (i > 0) {
                copyinvokers = list(invocation);
                //重新检查一下
                checkInvokers(copyinvokers, invocation);
            }
            Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked);
            invoked.add(invoker);
            RpcContext.getContext().setInvokers((List)invoked);
            try {
                Result result = invoker.invoke(invocation);
                return result;
            }catch(){}
        }

    }

详细说备援的过程:
1. 调用抛异常时,会进行重试,重试次数通过Constants.RETRIES_KEY配置
2. 重试时会重新list,并过滤掉已调用失败的节点,select(loadbalance, invocation, copyinvokers, invoked)里的invoked里记录了失败节点
3. 通过select(loadbalance, invocation, copyinvokers, invoked)在invoker list里做负载均衡
4. select出invoker后,直接做invoke,这里的invoker也是个filter Invoke,这时候就开始链式拦截。

最后看一下LoadBalance的接口描述

public interface LoadBalance {

    @Adaptive("loadbalance")
    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;

}

select方法是个Adaptive方法,会从url里提取loadbalance参数值做动态委派。负载均衡比较简单,这里不做进一步展开。

你可能感兴趣的:(负载均衡,架构,filter,Dubbo源码)