Dubbo Failover机制

Dubbo Failover机制背景 nbsp nbsp nbsp nbsp 某个服务有 2 台机器 但是其中一台机器挂了 dubbo 的重试还是会调用这台机器 经过查资料 推荐使用 Failover 机制 nbsp 过程 nbsp nbsp nbsp nbsp 经过跟踪源码 发现 dubbo 默认的容错机制就是使用了 Failover 在执行调用前会直接进入 FailoverClus class 的 doInvoke 中 翻开源码 publicResult

背景

       某个服务有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没有通知到消费者的话,那么消费者还是会继续根据的服务提供者的列表来访问。

 

如果有误,欢迎讨论

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224255.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午12:21
下一篇 2026年3月17日 下午12:21


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号