背景:
某个服务有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
