CoderLi

V1

2022/03/06阅读:49主题:前端之巅同款

Dubbo Cluster

Cluster 层

集群容错层、该层中包含 Cluster、Directory、Router、LoadBalance几大核心接口

@SPI(FailoverCluster.NAME)
public interface Cluster {
    @Adaptive
    <T> Invoker<T> join(Directory<T> directory) throws RpcException;
}
image-20220305143240818
image-20220305143240818

这个版本有 10 种容错机制

每个具体的 Cluster 实现都是创建一个对应的 Invoker、然后直接返回

Failover

当出现失败时、会重试其他服务器、用户可以通过 retries 参数设置重试次数、默认是两次。是 Dubbo 的默认容错机制。会对服务提供者列表做负载均衡、通常使用在读操作或者幂等写操作。但重试会导致接口延迟增大、增加下游服务器的负载。

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

从继承关系图中我们知道它的父类是 AbstractCluster 。 invoke 方法如下

@Override
public Result invoke(final Invocation invocation) throws RpcException {
    checkWhetherDestroyed();

    // binding attachments into invocation.
    Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
    if (contextAttachments != null && contextAttachments.size() != 0) {
        ((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
    }
  
    List<Invoker<T>> invokers = list(invocation);
    LoadBalance loadbalance = initLoadBalance(invokers, invocation);
    RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
    return doInvoke(invocation, invokers, loadbalance);
}
  • list(invocation) 从 Directory 中获取提供者的列表
  • initLoadBalance(invokers, invocation); 选择出负载均衡策略
  • doInvoke(invocation, invokers, loadbalance) 调用子类具体实现

很明显、这个是一个模板方法

public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    List<Invoker<T>> copyInvokers = invokers;
    checkInvokers(copyInvokers, invocation);
    String methodName = RpcUtils.getMethodName(invocation);
    int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, 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);
        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());
        }
    }
    throw new RpcException(le.getCode(), "Failed to invoke the method ");
}
  • checkInvokers(copyInvokers, invocation) 判断 Invoker 列表是否为空、为空则抛出异常
  • int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1; 获取用户配置的重试次数、如果没有则默认是重试 2 次、这里 +1 是因为正常调用
  • invoked 变脸用于存储已经发起调用过的 Invoker、也就是存在于这个集合里面的、都是调用过发生RPC异常的
  • 当你第一次调用失败之后、那么每一次循环都会重新从 Diretory 中获取最新的 Invoker 列表、因为调用次数是死的、已经失败过一次了、尽可能保证被调用的 Invoker 的质量
  • select(loadbalance, invocation, copyInvokers, invoked) 根据具体的负载均衡算法算出一个 Invoker

Failfast

快速失败、当请求失败之后、快速返回异常结、不做任何重试

@Override
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    checkInvokers(invokers, invocation);
    Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
    try {
        return invoker.invoke(invocation);
    } catch (Throwable e) {
        if (e instanceof RpcException && ((RpcException) e).isBiz()) { // biz exception.
            throw (RpcException) e;
        }
        throw new RpcException();
    }

负载均衡选出一个 Invoker 之后直接进行调用。如果有异常、直接抛出

这个通常使用在非幂等的接口中、但是该策略受网络抖动影响较大。

Failsafe

当出现异常时、直接忽略异常。通常对调用结果不关心、并且不想影响外层调用。比如说上传一些不重要的日志啊、监控数据啊

@Override
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    try {
        checkInvokers(invokers, invocation);
        Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
        return invoker.invoke(invocation);
    } catch (Throwable e) {
        return AsyncRpcResult.newDefaultAsyncResult(nullnull, invocation); // ignore
    }
}

Failback

请求失败之后、会自动记录在失败记录中、并有一个定时线程池定时重试、适用于一些异步或者最终一致性的请求

@Override
protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    Invoker<T> invoker = null;
    try {
        checkInvokers(invokers, invocation);
        invoker = select(loadbalance, invocation, invokers, null);
        return invoker.invoke(invocation);
    } catch (Throwable e) {
        addFailed(loadbalance, invocation, invokers, invoker);
        return AsyncRpcResult.newDefaultAsyncResult(nullnull, invocation); // ignore
    }
}
private void addFailed(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, Invoker<T> lastInvoker) {
    if (failTimer == null) {
        synchronized (this) {
            if (failTimer == null) {
                failTimer = new HashedWheelTimer(
                        new NamedThreadFactory("failback-cluster-timer"true),
                        1,
                        TimeUnit.SECONDS, 32, failbackTasks);
            }
        }
    }
    RetryTimerTask retryTimerTask = new RetryTimerTask(loadbalance, invocation, invokers, lastInvoker, retries, RETRY_FAILED_PERIOD);
    try {
        failTimer.newTimeout(retryTimerTask, RETRY_FAILED_PERIOD, TimeUnit.SECONDS);
    } catch (Throwable e) {
    }
}

@Override
public void run(Timeout timeout) {
  try {
    Invoker<T> retryInvoker = select(loadbalance, invocation, invokers, Collections.singletonList(lastInvoker));
    lastInvoker = retryInvoker;
    retryInvoker.invoke(invocation);
  } catch (Throwable e) {
    if ((++retryTimes) >= retries) {
    } else {
      rePut(timeout);
    }
  }
}

这个后续的调度重试涉及到时间轮的设计、但是我们在这里只要知道它也会重试、这里默认重试的次数是三次

Forking

同时调用多个相同的服务、只要其中一个返回、则立即返回结果。用户可以配置 forking 参数、来确定最大并行调用的服务数量。

通常使用在对接口实时性要求极高的调用上、但会浪费更多的资源

@Override
@SuppressWarnings({"unchecked""rawtypes"})
public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    try {
        checkInvokers(invokers, invocation);
        final List<Invoker<T>> selected;
        final int forks = getUrl().getParameter(FORKS_KEY, DEFAULT_FORKS);
        final int timeout = getUrl().getParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
        if (forks <= 0 || forks >= invokers.size()) {
            selected = invokers;
        } else {
            selected = new ArrayList<>(forks);
            while (selected.size() < forks) {
                Invoker<T> invoker = select(loadbalance, invocation, invokers, selected);
                if (!selected.contains(invoker)) {
                    selected.add(invoker);
                }
            }
        }
        RpcContext.getContext().setInvokers((List) selected);
        final AtomicInteger count = new AtomicInteger();
        final BlockingQueue<Object> ref = new LinkedBlockingQueue<>();
        for (final Invoker<T> invoker : selected) {
            executor.execute(() -> {
                try {
                    Result result = invoker.invoke(invocation);
                    ref.offer(result);
                } catch (Throwable e) {
                    int value = count.incrementAndGet();
                    if (value >= selected.size()) {
                        ref.offer(e);
                    }
                }
            });
        }
        try {
            Object ret = ref.poll(timeout, TimeUnit.MILLISECONDS);
            if (ret instanceof Throwable) {
                Throwable e = (Throwable) ret;
                throw new RpcException("");
            }
            return (Result) ret;
        } catch (InterruptedException e) {
            throw new RpcException();
        }
    } finally {
        // clear attachments which is binding to current thread.
        RpcContext.getContext().clearAttachments();
    }
}

默认最大的并行数是 2 个、超时时间是 1 秒。

第一步就是选出合适的 invoker、根据负载均衡算法。如果 forking 的数量大于等于 invoker 的数量、那么就直接不用选了

后续使用线程池异步发起调用、使用 BlockingQueue 对存储结果。最后使用 poll 等待结果、

Broadcast

广播调用所有可用服务、任意一个节点报错则报错。因为请求是调用所以节点、所以不需要做负载均衡

@Override
@SuppressWarnings({"unchecked""rawtypes"})
public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    checkInvokers(invokers, invocation);
    RpcContext.getContext().setInvokers((List) invokers);
    RpcException exception = null;
    Result result = null;
    for (Invoker<T> invoker : invokers) {
        try {
            result = invoker.invoke(invocation);
        } catch (RpcException e) {
            exception = e;
            logger.warn(e.getMessage(), e);
        } catch (Throwable e) {
            exception = new RpcException(e.getMessage(), e);
            logger.warn(e.getMessage(), e);
        }
    }
    if (exception != null) {
        throw exception;
    }
    return result;
}

Mock

提供者调用失败时、返回伪造的响应结果。或直接强制返回伪造结果、不会发起远程调用

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

    String value = getUrl().getMethodParameter(invocation.getMethodName(), MOCK_KEY, Boolean.FALSE.toString()).trim();
    if (value.length() == 0 || "false".equalsIgnoreCase(value)) {
        //no mock
        result = this.invoker.invoke(invocation);
    } else if (value.startsWith("force")) {
        if (logger.isWarnEnabled()) {
            logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + getUrl());
        }
        //force:direct mock
        result = doMockInvoke(invocation, null);
    } else {
        //fail-mock
        try {
            result = this.invoker.invoke(invocation);

            //fix:#4585
            if(result.getException() != null && result.getException() instanceof RpcException){
                RpcException rpcException= (RpcException)result.getException();
                if(rpcException.isBiz()){
                    throw  rpcException;
                }else {
                    result = doMockInvoke(invocation, rpcException);
                }
            }

        } catch (RpcException e) {
            if (e.isBiz()) {
                throw e;
            }
            result = doMockInvoke(invocation, e);
        }
    }
    return result;
}
  • 默认情况下、或者明确配置了 false、那么直接调用 invoker
  • 配置了 force 开头、那么直接走 mock 逻辑(相当于熔断了)
  • 除了上面的情况、invoker 失败或者异常的情况下、走 mock (相当于降级)

Available

最简单的方式、请求不会负载均衡、使用列表中的第一个 invoker

@Override
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    for (Invoker<T> invoker : invokers) {
        if (invoker.isAvailable()) {
            return invoker.invoke(invocation);
        }
    }
    throw new RpcException("No provider available in " + invokers);
}

Mergeable

自动把多个节点请求得到的结果进行合并

@Override
protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    checkInvokers(invokers, invocation);
    String merger = getUrl().getMethodParameter(invocation.getMethodName(), MERGER_KEY);
    if (ConfigUtils.isEmpty(merger)) { // If a method doesn't have a merger, only invoke one Group
        for (final Invoker<T> invoker : invokers) {
            if (invoker.isAvailable()) {
                try {
                    return invoker.invoke(invocation);
                } catch (RpcException e) {
                        throw e;
                }
            }
        }
        return invokers.iterator().next().invoke(invocation);
    }

    Class<?> returnType;
    try {
        returnType = getInterface().getMethod(
                invocation.getMethodName(), invocation.getParameterTypes()).getReturnType();
    } catch (NoSuchMethodException e) {
        returnType = null;
    }

    Map<String, Result> results = new HashMap<>();
    for (final Invoker<T> invoker : invokers) {
        RpcInvocation subInvocation = new RpcInvocation(invocation, invoker);
        subInvocation.setAttachment(ASYNC_KEY, "true");
        results.put(invoker.getUrl().getServiceKey(), invoker.invoke(subInvocation));
    }

    Object result = null;

    List<Result> resultList = new ArrayList<Result>(results.size());

    for (Map.Entry<String, Result> entry : results.entrySet()) {
        Result asyncResult = entry.getValue();
        try {
            Result r = asyncResult.get();
            if (r.hasException()) {
            } else {
                resultList.add(r);
            }
        } catch (Exception e) {
            throw new RpcException("Failed to invoke service " + entry.getKey() + ": " + e.getMessage(), e);
        }
    }

    if (resultList.isEmpty()) {
        return AsyncRpcResult.newDefaultAsyncResult(invocation);
    } else if (resultList.size() == 1) {
        return resultList.iterator().next();
    }

    if (returnType == void.class{
        return AsyncRpcResult.newDefaultAsyncResult(invocation);
    }

    if (merger.startsWith(".")) {
        merger = merger.substring(1);
        ..........
    } else {
        Merger resultMerger;
        if (ConfigUtils.isDefault(merger)) {
            resultMerger = MergerFactory.getMerger(returnType);
        } else {
            resultMerger = ExtensionLoader.getExtensionLoader(Merger.class).getExtension(merger);
        }
        if (resultMerger != null) {
            List<Object> rets = new ArrayList<Object>(resultList.size());
            for (Result r : resultList) {
                rets.add(r.getValue());
            }
            result = resultMerger.merge(
                    rets.toArray((Object[]) Array.newInstance(returnType, 0)));
        } else {
            throw new RpcException("There is no merger to merge result.");
        }
    }
    return AsyncRpcResult.newDefaultAsyncResult(result, invocation);
}

调用所有的 invoker、设置为异步调用。后续对返回的结果进行处理合并

内置的合并器

image-20220305201657819
image-20220305201657819

Zone

这个是新版本家的一个集群策略、主要是真的注册中心的负载、也即是对多注册中心进行负载均衡

public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
    for (Invoker<T> invoker : invokers) {
        ClusterInvoker<T> clusterInvoker = (ClusterInvoker<T>) invoker;
        if (clusterInvoker.isAvailable() && clusterInvoker.getRegistryUrl()
                .getParameter(REGISTRY_KEY + "." + PREFERRED_KEY, false)) {
            return clusterInvoker.invoke(invocation);
        }
    }
    String zone = invocation.getAttachment(REGISTRY_ZONE);
    if (StringUtils.isNotEmpty(zone)) {
        for (Invoker<T> invoker : invokers) {
            ClusterInvoker<T> clusterInvoker = (ClusterInvoker<T>) invoker;
            if (clusterInvoker.isAvailable() && zone.equals(clusterInvoker.getRegistryUrl().getParameter(REGISTRY_KEY + "." + ZONE_KEY))) {
                return clusterInvoker.invoke(invocation);
            }
        }
        String force = invocation.getAttachment(REGISTRY_ZONE_FORCE);
        if (StringUtils.isNotEmpty(force) && "true".equalsIgnoreCase(force)) {
            throw new IllegalStateException();
        }
    }
    // load balance among all registries, with registry weight count in.
    Invoker<T> balancedInvoker = select(loadbalance, invocation, invokers, null);
    if (balancedInvoker.isAvailable()) {
        return balancedInvoker.invoke(invocation);
    }
    for (Invoker<T> invoker : invokers) {
        ClusterInvoker<T> clusterInvoker = (ClusterInvoker<T>) invoker;
        if (clusterInvoker.isAvailable()) {
            return clusterInvoker.invoke(invocation);
        }
    }
    return invokers.get(0).invoke(invocation);
}
  • 如果该 Invoker 中有 preferred 值为 true 、那么就直接选该注册中心

  • 如果消费者和服务注册中心注册地址是在同一个 zone 的话、那么优先选择该服务注册中心

  • 如果都没有配置、那么就根据负载均衡算法选出一个

分类:

后端

标签:

后端

作者介绍

CoderLi
V1