Dubbo Failover机制

背景

       某个服务有2台机器,但是其中一台机器挂了,dubbo的重试还是会调用这台机器,经过查资料,推荐使用Failover机制;

 

过程:

        经过跟踪源码,发现dubbo默认的容错机制就是使用了Failover,在执行调用前会直接进入FailoverClusterInvoker.class的doInvoke中,翻开源码:

public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
        List> copyinvokers = invokers;
        this.checkInvokers(invokers, invocation);
        int len = this.getUrl().getMethodParameter(invocation.getMethodName(), "retries", 2) + 1;
        if (len <= 0) {
            len = 1;
        }

        RpcException le = null;
        List> invoked = new ArrayList(invokers.size());
        //1.记录执行rpc方法失败的提供者,但是实际不作为下次选取服务提供者的参考
        Set providers = new HashSet(len);

        for(int i = 0; i < len; ++i) {
           //第一次选取提供端的时候,不进行检测
            if (i > 0) {
                this.checkWhetherDestroyed();
                copyinvokers = this.list(invocation);
                this.checkInvokers(copyinvokers, invocation);
            }
           //2. 根据上次访问的提供者,进行负载均衡,即:如果此时是串行调用,那么下次不会选取上次的提供者
            Invoker invoker = this.select(loadbalance, invocation, copyinvokers, invoked);
            invoked.add(invoker);
            RpcContext.getContext().setInvokers(invoked);

            try {
                Result result = invoker.invoke(invocation);
                if (le != null && logger.isWarnEnabled()) {
                    logger.warn("Although retry the method " + invocation.getMethodName() + " in the service " + this.getInterface().getName() + " was successful by the provider " + invoker.getUrl().getAddress() + ", but there have been failed providers " + providers + " (" + providers.size() + "/" + copyinvokers.size() + ") from the registry " + this.directory.getUrl().getAddress() + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + ". Last error is: " + le.getMessage(), le);
                }

                Result var12 = result;
                return var12;
            } catch (RpcException var17) {
                if (var17.isBiz()) {
                    throw var17;
                }

                le = var17;
            } catch (Throwable var18) {
                le = new RpcException(var18.getMessage(), var18);
            } finally {
                providers.add(invoker.getUrl().getAddress());
            }
        }

        throw new RpcException(le != null ? le.getCode() : 0, "Failed to invoke the method " + invocation.getMethodName() + " in the service " + this.getInterface().getName() + ". Tried " + len + " times of the providers " + providers + " (" + providers.size() + "/" + copyinvokers.size() + ") from the registry " + this.directory.getUrl().getAddress() + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version " + Version.getVersion() + ". Last error is: " + (le != null ? le.getMessage() : ""), (Throwable)(le != null && le.getCause() != null ? le.getCause() : le));
    }

AbstractClusterInvoker中的doselect关键源码:

//AbstractClusterInvoker
private Invoker doselect(LoadBalance loadbalance, Invocation invocation, List> invokers, List> selected) throws RpcException {
        if (invokers != null && !invokers.isEmpty()) {
            if (invokers.size() == 1) {
                return (Invoker)invokers.get(0);
            } else if (invokers.size() == 2 && selected != null && !selected.isEmpty()) {
                return selected.get(0) == invokers.get(0) ? (Invoker)invokers.get(1) : (Invoker)invokers.get(0);
            } else {
                //loadbalance 随机函数,里面是根据一个随机数来判断此次调用选择哪个提供者
                Invoker invoker = loadbalance.select(invokers, this.getUrl(), invocation);
                //这个时候如果zk通知消费者,提供者挂了的话,那么此时将会重新选择一个提供者进行调用
                if (selected != null && selected.contains(invoker) || !invoker.isAvailable() && this.getUrl() != null && this.availablecheck) {
                    try {
                        Invoker rinvoker = this.reselect(loadbalance, invocation, invokers, selected, this.availablecheck);
                        if (rinvoker != null) {
                            invoker = rinvoker;
                        } else {
                            int index = invokers.indexOf(invoker);

                            try {
                                invoker = index < invokers.size() - 1 ? (Invoker)invokers.get(index + 1) : invoker;
                            } catch (Exception var9) {
                                logger.warn(var9.getMessage() + " may because invokers list dynamic change, ignore.", var9);
                            }
                        }
                    } catch (Throwable var10) {
                        logger.error("clustor relselect fail reason is :" + var10.getMessage() + " if can not slove ,you can set cluster.availablecheck=false in url", var10);
                    }
                }

                return invoker;
            }
        } else {
            return null;
        }
    }

 

结果:

      根据这段源码,同时重点关注里面的注释1、注释2,可知,failover其实在一次调用过程中不会根据以往的失败记录作为参看条件,只是在负载均衡的大条件下,来选取服务提供者,所以实际failover并不一定能做到切换服务提供者。

       其次,如果单次调用,服务超时了,那么此时还是继续调用该服务,不会换机器

       最后,根据select中的源码可以发现,如果服务挂了,但是zk没有通知到消费者的话,那么消费者还是会继续根据的服务提供者的列表来访问。

 

如果有误,欢迎讨论

你可能感兴趣的:(dubbo源码)