dubbo——容错模式

一、dubbo提供6种容错模式

  • failover:默认模式。调用失败时,自动切换重试,可通过retries属性设置重试次数。适用于读操作,每次读数据都相同。
  • failfast:快速失败模式。只调用一次,调用失败时立即报错。适用于写操作,不能重复写。
  • failsafe:安全失败模式。只调用一次,调用失败时忽略失败的调用,记录日志。
  • failback:失败恢复模式。在失败后自动恢复,后台记录失败的请求,定时重发。通常用于消息通知。
  • forking:并行调用多个服务器,只要有一个成功便返回。通常用于实时性要求较高的读操作,会浪费服务资源。
  • broadcast:广播调用所有的提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

二、FailOverClusterInvoker——失败重试

/* org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker */
public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        List<Invoker<T>> copyInvokers = invokers;
        //检查providers不能为空
        checkInvokers(copyInvokers, invocation);
        //获取调用的方法名
        String methodName = RpcUtils.getMethodName(invocation);
        //调用次数  1+2次重试
        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++) {
            //失败重试,成功返回
            if (i > 0) {
                //再次检查invokers.destroyed
                checkWhetherDestroyed();
                copyInvokers = list(invocation);
                // check again
                checkInvokers(copyInvokers, invocation);
            }
            //负载均衡策略,选取一个invoker
            Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
            //标记已经调用过的invoker,重试时,换其他invoker重试
            invoked.add(invoker);
            RpcContext.getContext().setInvokers((List) invoked);
            try {
                Result result = invoker.invoke(invocation);
                if (le != null && logger.isWarnEnabled()) {
                    logger.warn("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);
                }
                //成功返回
                return result;
                //catch报错,失败重试
            } 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 "
                + 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);
    }

三、FailfastClusterInvoker——失败抛错

/* org.apache.dubbo.rpc.cluster.support.FailfastClusterInvoker#doInvoke */
    public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        //检查invokers不能为空
        checkInvokers(invokers, invocation);
        //负载均衡策略,选取一个invoker
        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(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);
        }
    }

四、FaisafeClusterInvoker——失败忽略,记录日志

/* org.apache.dubbo.rpc.cluster.support.FailsafeClusterInvoker#doInvoke */
    public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        try {
            //检查invokers不能为空
            checkInvokers(invokers, invocation);
            //负载均衡策略,选取一个invoker
            Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
            //调用
            return invoker.invoke(invocation);
        } catch (Throwable e) {
            //报错忽略
            logger.error("Failsafe ignore exception: " + e.getMessage(), e);
            return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
        }
    }

五、FailbackClusterInvoker——失败恢复,记录失败请求,定时重发

/* org.apache.dubbo.rpc.cluster.support.FailbackClusterInvoker#doInvoke */
    protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        Invoker<T> invoker = null;
        try {
            //检查invokers不能为空
            checkInvokers(invokers, invocation);
            //负载均衡策略,选取一个invoker
            invoker = select(loadbalance, invocation, invokers, null);
            //调用
            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);
            //创建定时任务TimeTask实现
            addFailed(loadbalance, invocation, invokers, invoker);
            //返回记录失败日志
            return AsyncRpcResult.newDefaultAsyncResult(null, null, invocation); // ignore
        }
    }

六、ForkingClusterInvoker——并行调用,有一个调用成功便返回

/* org.apache.dubbo.rpc.cluster.support.ForkingClusterInvoker#doInvoke */
public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        try {
            //检查invokers不能为空
            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()) {
                //并发数forks大于invokers.size()时,并发调用所有invoker
                selected = invokers;
            } else {
                //并发数forks小于invokers.size()时,调用负载均衡策略选取的forks个invoker
                selected = new ArrayList<>();
                for (int i = 0; i < forks; i++) {
                    Invoker<T> invoker = select(loadbalance, invocation, invokers, selected);
                    if (!selected.contains(invoker)) {
                        //Avoid add the same invoker several times.
                        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(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);
            }
        } finally {
            // clear attachments which is binding to current thread.
            RpcContext.getContext().clearAttachments();
        }
    }

七、BroadcastClusterInvoker——串行调用,所有调用成功则成功,有一台报错则抛错

/* org.apache.dubbo.rpc.cluster.support.BroadcastClusterInvoker#doInvoke */
    public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
        //检查invokers不能为空
        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);
            }
        }
        //有一个invoker调用抛错,则抛出错误
        if (exception != null) {
            throw exception;
        }
        return result;
    }

 

 

 

posted on 2020-03-16 17:10  FFStayF  阅读(484)  评论(0编辑  收藏  举报