Dubbo源码分析(十二) 集群容错

下面我们来分析一下Dubbo的集群容错机制。我们先来看一下各个节点之间的关系
这里的Invoker是Provider的一个可调用Service的抽象,Invoker封装了Provider地址及Service接口信息。
Directory代表多个Invoker,可以把它看成List,但与List不同的是,它的值可能是动态变化的,比如
注册中心推送变更。
Cluster将Directory中的多个Invoker伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败
后,重试另一个。
Router负责从多个Invoker中按路由规则选出子集,比如读写分离,应用隔离等。
LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败
后,需要重选。
再来说一下Dubbo的集群的容错模式

Failover Cluster
  • 失败自动切换,当出现失败,重试其它服务器。(缺省)
  • 通常用于读操作,但重试会带来更长延迟。
  • 可通过retries="2"来设置重试次数(不含第一次)。
Failfast Cluster
  • 快速失败,只发起一次调用,失败立即报错。
  • 通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster
  • 失败安全,出现异常时,直接忽略。
  • 通常用于写入审计日志等操作。
Failback Cluster
  • 失败自动恢复,后台记录失败请求,定时重发。
  • 通常用于消息通知操作。
Forking Cluster
  • 并行调用多个服务器,只要一个成功即返回。
  • 通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
  • 可通过forks="2"来设置最大并行数。
Broadcast Cluster
  • 广播调用所有提供者,逐个调用,任意一台报错则报错。(2.1.0开始支持)

  • 通常用于通知所有提供者更新缓存或日志等本地资源信息。
    重试次数配置如:(failover集群模式生效)

    
    

或:

   

或:

   

   

   

集群模式配置如:

   

或:

  

那么Dubbo的集群容错是怎么实现的呢,现在我们来看一下。
先看一下Cluster接口

/**
 * Merge the directory invokers to a virtual invoker.
 *
 * 基于 Directory ,创建 Invoker 对象,实现统一、透明的 Invoker 调用过程
 *
 * @param directory Directory 对象
 * @param   泛型
 * @return cluster invoker
 * @throws RpcException
 */
@Adaptive
 Invoker join(Directory directory) throws RpcException;

下面我们来介绍几个Cluster的实现类
AvailableCluster,失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

public  Invoker join(Directory directory) throws RpcException {
    return new AvailableClusterInvoker(directory);
}

看一下AvailableClusterInvoker这个类

public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
    // 循环候选的 Invoker 集合,调用首个可用的 Invoker 对象。
    for (Invoker invoker : invokers) {
        if (invoker.isAvailable()) { // 可用
            // 发起 RPC 调用
            return invoker.invoke(invocation);
        }
    }
    throw new RpcException("No provider available in " + invokers);
}

BroadcastCluster,广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

public  Invoker join(Directory directory) throws RpcException {
    return new BroadcastClusterInvoker(directory);
}

看一下BroadcastClusterInvoker的实现

public Result doInvoke(final Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
    // 检查 invokers 即可用Invoker集合是否为空,如果为空,那么抛出异常
    checkInvokers(invokers, invocation);
    // 设置已经调用的 Invoker 集合,到 Context 中
    RpcContext.getContext().setInvokers((List) invokers);
    // 保存最后一次调用的异常
    RpcException exception = null;
    // 保存最后一次调用的结果
    Result result = null;
    // 循环候选的 Invoker 集合,调用所有 Invoker 对象。
    for (Invoker invoker : invokers) {
        try {
            // 发起 RPC 调用
            result = invoker.invoke(invocation);
        } catch (RpcException e) {
            exception = e;
            logger.warn(e.getMessage(), e);
        } catch (Throwable e) {
            exception = new RpcException(e.getMessage(), e); // 封装成 RpcException 异常
            logger.warn(e.getMessage(), e);
        }
    }
    // 若存在一个异常,抛出该异常
    if (exception != null) {
        throw exception;
    }
    return result;
}

FailbackCluster,失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

public  Invoker join(Directory directory) throws RpcException {
    return new FailbackClusterInvoker(directory);
}

看一下FailbackClusterInvoker的实现

protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
    try {
        // 检查 invokers 即可用Invoker集合是否为空,如果为空,那么抛出异常
        checkInvokers(invokers, invocation);
        // 根据负载均衡机制从 invokers 中选择一个Invoker
        Invoker invoker = select(loadbalance, invocation, invokers, null);
        // RPC 调用得到 Result
        return invoker.invoke(invocation);
    } catch (Throwable e) {
        logger.error("Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: " + e.getMessage() + ", ", e);
        // 添加到失败任务
        addFailed(invocation, this);
        return new RpcResult(); // ignore
    }
}

FailfastCluster,快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

public  Invoker join(Directory directory) throws RpcException {
    return new FailfastClusterInvoker(directory);
}

看一下FailfastClusterInvoker的实现

public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
    // 检查 invokers 即可用Invoker集合是否为空,如果为空,那么抛出异常
    checkInvokers(invokers, invocation);
    // 根据负载均衡机制从 invokers 中选择一个Invoker
    Invoker invoker = select(loadbalance, invocation, invokers, null);
    try {
        // RPC 调用得到 Result
        return invoker.invoke(invocation);
    } catch (Throwable e) {
        // 若是业务性质的异常,直接抛出
        if (e instanceof RpcException && ((RpcException) e).isBiz()) { // biz exception.
            throw (RpcException) e;
        }
        // 封装 RpcException 异常,并抛出
        throw new RpcException(e instanceof RpcException ? ((RpcException) e).getCode() : 0,
                "Failfast invoke providers " + invoker.getUrl() + " " + loadbalance.getClass().getSimpleName() + " select from all providers " + invokers + " for service " + getInterface().getName() + " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e.getCause() != null ? e.getCause() : e);
    }
}

FailoverCluster,败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

public  Invoker join(Directory directory) throws RpcException {
    return new FailoverClusterInvoker(directory);
}

看下FailoverClusterInvoker的实现

public Result doInvoke(Invocation invocation, final List> invokers, LoadBalance loadbalance) throws RpcException {
    List> 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> invoked = new ArrayList>(copyinvokers.size()); // invoked invokers.
    Set providers = new HashSet(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 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;
            }
            // 其他性质的异常统一封装成RpcException
            le = e;
        } catch (Throwable e) {
            le = new RpcException(e.getMessage(), e);
        } finally {
            providers.add(invoker.getUrl().getAddress());
        }
    }
    // 最大可调用次数用完还得到Result的话,抛出RpcException异常:重试了N次还是失败,并输出最后一次异常信息
    throw new RpcException(le.getCode(), "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.getMessage(), le.getCause() != null ? le.getCause() : le);
}

FailsafeCluster,失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

public  Invoker join(Directory directory) throws RpcException {
    return new FailsafeClusterInvoker(directory);
}

看一下FailsafeClusterInvoker的实现

public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
    try {
        // 检查 invokers 即可用Invoker集合是否为空,如果为空,那么抛出异常
        checkInvokers(invokers, invocation);
        // 根据负载均衡机制从 invokers 中选择一个Invoker
        Invoker invoker = select(loadbalance, invocation, invokers, null);
        // RPC 调用得到 Result
        return invoker.invoke(invocation);
    } catch (Throwable e) {
        // 打印异常日志
        logger.error("Failsafe ignore exception: " + e.getMessage(), e);
        // 忽略异常
        return new RpcResult(); // ignore
    }
}

ForkingCluster,并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

public  Invoker join(Directory directory) throws RpcException {
    return new ForkingClusterInvoker(directory);
}

看一下ForkingClusterInvoker的实现

public Result doInvoke(final Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
    // 检查 invokers 即可用Invoker集合是否为空,如果为空,那么抛出异常
    checkInvokers(invokers, invocation);
    // 保存选择的 Invoker 集合
    final List> selected;
    // 得到最大并行数,默认为 Constants.DEFAULT_FORKS = 2
    final int forks = getUrl().getParameter(Constants.FORKS_KEY, Constants.DEFAULT_FORKS);
    // 获得调用超时时间,默认为 DEFAULT_TIMEOUT = 1000 毫秒
    final int timeout = getUrl().getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
    // 若最大并行书小于等于 0,或者大于 invokers 的数量,直接使用 invokers
    if (forks <= 0 || forks >= invokers.size()) {
        selected = invokers;
    } else {
        // 循环,根据负载均衡机制从 invokers,中选择一个个Invoker ,从而组成 Invoker 集合。
        // 注意,因为增加了排重逻辑,所以不能保证获得的 Invoker 集合的大小,小于最大并行数
        selected = new ArrayList>();
        for (int i = 0; i < forks; i++) {
            // 在invoker列表(排除selected)后,如果没有选够,则存在重复循环问题.见select实现.
            Invoker invoker = select(loadbalance, invocation, invokers, selected);
            if (!selected.contains(invoker)) { //Avoid add the same invoker several times. //防止重复添加invoker
                selected.add(invoker);
            }
        }
    }
    // 设置已经调用的 Invoker 集合,到 Context 中
    RpcContext.getContext().setInvokers((List) selected);
    // 异常计数器
    final AtomicInteger count = new AtomicInteger();
    // 创建阻塞队列
    final BlockingQueue ref = new LinkedBlockingQueue();
    // 循环 selected 集合,提交线程池,发起 RPC 调用
    for (final Invoker invoker : selected) {
        executor.execute(new Runnable() {
            public void run() {
                try {
                    // RPC 调用,获得 Result 结果
                    Result result = invoker.invoke(invocation);
                    // 添加 Result 到 `ref` 阻塞队列
                    ref.offer(result);
                } catch (Throwable e) {
                    // 异常计数器 + 1
                    int value = count.incrementAndGet();
                    // 若 RPC 调结果都是异常,则添加异常到 `ref` 阻塞队列
                    if (value >= selected.size()) {
                        ref.offer(e);
                    }
                }
            }
        });
    }
    try {
        // 从 `ref` 队列中,阻塞等待结果
        Object ret = ref.poll(timeout, TimeUnit.MILLISECONDS);
        // 若是异常结果,抛出 RpcException 异常
        if (ret instanceof Throwable) {
            Throwable e = (Throwable) ret;
            throw new RpcException(e instanceof RpcException ? ((RpcException) e).getCode() : 0, "Failed to forking invoke provider " + selected + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e.getCause() != null ? e.getCause() : e);
        }
        // 若是正常结果,直接返回
        return (Result) ret;
    } catch (InterruptedException e) {
        throw new RpcException("Failed to forking invoke provider " + selected + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e);
    }
}

MergeableCluster,分组聚合 Cluster 实现类

public  Invoker join(Directory directory) throws RpcException {
    return new MergeableClusterInvoker(directory);
}

看一下MergeableClusterInvoker的实现

public Result invoke(final Invocation invocation) throws RpcException {
    // 获得 Invoker 集合
    List> invokers = directory.list(invocation);
    // 获得 Merger 拓展名
    String merger = getUrl().getMethodParameter(invocation.getMethodName(), Constants.MERGER_KEY);
    // 若果未配置拓展,直接调用首个可用的 Invoker 对象
    if (ConfigUtils.isEmpty(merger)) { // If a method doesn't have a merger, only invoke one Group
        for (final Invoker invoker : invokers) {
            if (invoker.isAvailable()) {
                return invoker.invoke(invocation);
            }
        }
        return invokers.iterator().next().invoke(invocation);
    }

    // 通过反射,获得返回类型
    Class returnType;
    try {
        returnType = getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()).getReturnType();
    } catch (NoSuchMethodException e) {
        returnType = null;
    }

    // 提交线程池,并行执行,发起 RPC 调用,并添加到 results 中
    Map> results = new HashMap>();
    for (final Invoker invoker : invokers) {
        Future future = executor.submit(new Callable() {
            public Result call() {
                // RPC 调用
                return invoker.invoke(new RpcInvocation(invocation, invoker));
            }
        });
        results.put(invoker.getUrl().getServiceKey(), future);
    }

    // 阻塞等待执行执行结果,并添加到 resultList 中
    List resultList = new ArrayList(results.size());
    int timeout = getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
    for (Map.Entry> entry : results.entrySet()) {
        Future future = entry.getValue();
        try {
            Result r = future.get(timeout, TimeUnit.MILLISECONDS);
            if (r.hasException()) { // 异常 Result ,打印错误日志,忽略
                log.error(new StringBuilder(32).append("Invoke ").append(getGroupDescFromServiceKey(entry.getKey())).append(" failed: ").append(r.getException().getMessage()).toString(), r.getException());
            } else { // 正常 Result ,添加到 resultList 中
                resultList.add(r);
            }
        } catch (Exception e) { // 异常,抛出 RpcException 异常
            throw new RpcException(new StringBuilder(32).append("Failed to invoke service ").append(entry.getKey()).append(": ").append(e.getMessage()).toString(), e);
        }
    }

    // 结果大小为空,返回空的 RpcResult
    if (resultList.isEmpty()) {
        return new RpcResult((Object) null);
    // 结果大小为 1 ,返回首个 RpcResult
    } else if (resultList.size() == 1) {
        return resultList.iterator().next();
    }
    // 返回类型为 void ,返回空的 RpcResult
    if (returnType == void.class) {
        return new RpcResult((Object) null);
    }

    Object result;
    // 【第 1 种】基于合并方法
    if (merger.startsWith(".")) {
        // 获得合并方法 Method
        merger = merger.substring(1);
        Method method;
        try {
            method = returnType.getMethod(merger, returnType);
        } catch (NoSuchMethodException e) {
            throw new RpcException(new StringBuilder(32).append("Can not merge result because missing method [ ").append(merger).append(" ] in class [ ").append(returnType.getClass().getName()).append(" ]").toString());
        }
        // 有 Method ,进行合并
        if (method != null) {
            if (!Modifier.isPublic(method.getModifiers())) {
                method.setAccessible(true);
            }
            result = resultList.remove(0).getValue();
            try {
                // 方法返回类型匹配,合并时,修改 result
                if (method.getReturnType() != void.class && method.getReturnType().isAssignableFrom(result.getClass())) {
                    for (Result r : resultList) {
                        result = method.invoke(result, r.getValue());
                    }
                // 方法返回类型不匹配,合并时,不修改 result
                } else {
                    for (Result r : resultList) {
                        method.invoke(result, r.getValue());
                    }
                }
            } catch (Exception e) {
                throw new RpcException(new StringBuilder(32).append("Can not merge result: ").append(e.getMessage()).toString(), e);
            }
        // 无 Method ,抛出 RpcException 异常
        } else {
            throw new RpcException(new StringBuilder(32).append("Can not merge result because missing method [ ").append(merger).append(" ] in class [ ").append(returnType.getClass().getName()).append(" ]").toString());
        }
    // 【第 2 种】基于 Merger
    } else {
        Merger resultMerger;
        // 【第 2.1 种】根据返回值类型自动匹配 Merger
        if (ConfigUtils.isDefault(merger)) {
            resultMerger = MergerFactory.getMerger(returnType);
        // 【第 2.2 种】指定 Merger
        } else {
            resultMerger = ExtensionLoader.getExtensionLoader(Merger.class).getExtension(merger);
        }
        // 有 Merger ,进行合并
        if (resultMerger != null) {
            List rets = new ArrayList(resultList.size());
            for (Result r : resultList) {
                rets.add(r.getValue());
            }
            result = resultMerger.merge(rets.toArray((Object[]) Array.newInstance(returnType, 0)));
        // 无 Merger ,抛出 RpcException 异常
        } else {
            throw new RpcException("There is no merger to merge result.");
        }
    }
    // 返回 RpcResult 结果
    return new RpcResult(result);
}

Dubbo的集群容错就介绍到这里了。

你可能感兴趣的:(Dubbo源码分析(十二) 集群容错)