Dubbo的集群容错策略剖析

1 Dubbo的集群容错策略概述

Dubbo的集群容错策略应用于服务消费方,并在服务消费方进行设置当服务消费方调用服务提供方的服务失败时,Dubbo提供了多种集群容错策略来处理失败。其中默认的策略为“失败重试”策略,即Failover Cluster。下面将对多种集群容错策略依次进行解读。

2 Dubbo的集群容错策略源码分析

2.1 失败重试-Failover Cluster

(1)概述

  • 要点:当服务消费方调用服务提供方的服务失败时,消费方会自动切换到其他服务提供者服务器进行重试。
  • 适用场景:读操作或者具有幂等的写操作
  • 注意事项:重试会带来更长的延迟。
  • 使用方式:通过设置 retries 参数。如 retries=“3” 即重试3次(不含第一次调用),即最多调用4次。另外可以设置接口级别或者方法级别的重试次数。不主动设置时,默认的重试次数为2次

(2)源码分析

实现失败重试的类是FailoverClusterInvoker,主要的方法是其doInvoke()方法。具体如下所示。

public class FailoverCluster extends AbstractCluster {

    public final static String NAME = "failover";

    @Override
    public  AbstractClusterInvoker doJoin(Directory directory) throws RpcException {
        return new FailoverClusterInvoker<>(directory);
    }

}


// FailoverClusterInvoker 的 doInvoke()方法
public Result doInvoke(Invocation invocation, final List> invokers, LoadBalance loadbalance) throws RpcException {
    // 所有的服务提供者
    List> copyInvokers = invokers;
    String methodName = RpcUtils.getMethodName(invocation);

    // 计算最多调用多少次(retries+1)
    int len = calculateInvokeTimes(methodName);
    // retry loop.
    RpcException le = null;
    List> invoked = new ArrayList>(copyInvokers.size()); // invoked invokers.
    Set providers = new HashSet(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 invoker = select(loadbalance, invocation, copyInvokers, invoked);
        invoked.add(invoker);
        RpcContext.getServiceContext().setInvokers((List) invoked);
        boolean success = false;
        try {
            // 发起远程调用
            Result result = invokeWithContext(invoker, invocation);
            if (le != null && logger.isWarnEnabled()) {
                logger.warn(CLUSTER_FAILED_MULTIPLE_RETRIES,"failed to retry do invoke","","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);
            }
            success = true;
            return result;
        } catch (RpcException e) {
            // 如果为 biz exception(BIZ_EXCEPTION)则不重试
            if (e.isBiz()) {
                throw e;
            }
            le = e;
        } catch (Throwable e) {
            le = new RpcException(e.getMessage(), e);
        } finally {
            if (!success) {
                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);
}

// 计算最多调用多少次
private int calculateInvokeTimes(String methodName) {
    int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
    RpcContext rpcContext = RpcContext.getClientAttachment();
    Object retry = rpcContext.getObjectAttachment(RETRIES_KEY);
    if (retry instanceof Number) {
        len = ((Number) retry).intValue() + 1;
        rpcContext.removeAttachment(RETRIES_KEY);
    }
    if (len <= 0) {
        len = 1;
    }

    return len;
}

// 发起远程调用
protected Result invokeWithContext(Invoker invoker, Invocation invocation) {
    Invoker originInvoker = setContext(invoker);
    Result result;
    try {
        if (ProfilerSwitch.isEnableSimpleProfiler()) {
            InvocationProfilerUtils.enterProfiler(invocation, "Invoker invoke. Target Address: " + invoker.getUrl().getAddress());
        }
        setRemote(invoker, invocation);
        result = invoker.invoke(invocation);
    } finally {
        clearContext(originInvoker);
        InvocationProfilerUtils.releaseSimpleProfiler(invocation);
    }
    return result;
}



2.2 快速失败-Failfast Cluster

未完待续

你可能感兴趣的:(源码研究-Dubbo,3.2.7,dubbo,集群容错,ClusterInvoker)