(三)集群容错
来源:互联网 发布:淘宝访客来源 网址 编辑:程序博客网 时间:2024/06/05 15:21
集群容错
1.失败转移
在源码的“3处”,会用loadbalance负载均衡策略选择出一个提供者,在“4处”,如果被选中的提供者,已经被选择过,则会重新选择一个提供者(调用reselect方法)。
如果只有两个提供者,则集群方式变成轮询,即是第1次失败,则第2次重试会调用另一个提供者
“21处”,选择一个提供者,如何选择一个提供者,这是由负载均衡部分来实现的,可以看上面源码的doselect方法。
“22处”,这里只调用了一次,成功则返回结果,失败则抛出异常。
线程池里,会异步地调用每一个提供者,
“41处”,循环调用列表中的每一个提供者,不管调用是有结果还是抛出异常,都会放到队列里,
“42处”,如果有结果返回则放到LinkedBlockingQueue队列里,
“43处”,如果调用抛出异常,也相当于有结果返回,也会往队列放一个异常结果。
“44处”,队列阻塞直到有结果,如果是异常,则会抛出异常,有结果则返回。
“51处”,会根据负载均衡策略得到一个提供者,调用提供者。如果调用失败异常了,会执行“52处”,如果调用异常,则加入异常Map中,以备定时器定时重新调用提供者。
调用异常进入方法addFailed,在“53处”,第一次调用addFailed方法时,retryFutrue为null,会启动定时器scheduledExecutorService,接着在源码“54处”,定时器5000毫秒执行一次retryFailed方法,retryFailed方法会循环failed Map对象里的失败调用,再次调用。在“55处”,就会将调用放入Map对象failed中。
1.失败转移
配置:cluster="failover"
/** * 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。 * <a href="http://en.wikipedia.org/wiki/Failover">Failover</a> * @author william.liangf */public class FailoverCluster implements Cluster { public final static String NAME = "failover"; public <T> Invoker<T> join(Directory<T> directory) throws RpcException { return new FailoverClusterInvoker<T>(directory); }}public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> { public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException { List<Invoker<T>> copyinvokers = invokers; checkInvokers(copyinvokers, invocation); int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.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++) { //重试时,进行重新选择,避免重试时invoker列表已发生变化. //注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变 if (i > 0) { checkWhetherDestroyed(); copyinvokers = list(invocation); //重新检查一下 checkInvokers(copyinvokers, invocation); } //1处,从提供者列表中选择出一个提供者 Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked); //2处,加入已选择的提供者列表 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 " + invocation.getMethodName() + " 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 (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 != null ? le.getCode() : 0, "Failed to invoke the method " + invocation.getMethodName() + " 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 != null ? le.getMessage() : ""), le != null && le.getCause() != null ? le.getCause() : le); }}
看源码的“1处”,会从提供者列表中选择一个提供者,并且在“2处”,加入已选择列表,这便于重新选择提供者,避开已选过提供者。
下面看看选择一个提供者的实现方法的源码:
protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException { if (invokers == null || invokers.size() == 0) return null; String methodName = invocation == null ? "" : invocation.getMethodName(); boolean sticky = invokers.get(0).getUrl().getMethodParameter(methodName,Constants.CLUSTER_STICKY_KEY, Constants.DEFAULT_CLUSTER_STICKY) ; { //ignore overloaded method if ( stickyInvoker != null && !invokers.contains(stickyInvoker) ){ stickyInvoker = null; } //ignore cucurrent problem if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))){ if (availablecheck && stickyInvoker.isAvailable()){ return stickyInvoker; } } } Invoker<T> invoker = doselect(loadbalance, invocation, invokers, selected); if (sticky){ stickyInvoker = invoker; } return invoker;}private Invoker<T> doselect(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException { if (invokers == null || invokers.size() == 0) return null; if (invokers.size() == 1) return invokers.get(0); // 如果只有两个invoker,退化成轮循 if (invokers.size() == 2 && selected != null && selected.size() > 0) { return selected.get(0) == invokers.get(0) ? invokers.get(1) : invokers.get(0); } //3处,loadbalance负载均衡策略选择出一个提供者 Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation); //4处,如果被选中的提供者,已经被选择过,则会重新选择一个提供者(调用reselect方法)。 //如果 selected中包含(优先判断) 或者 不可用&&availablecheck=true 则重试. if( (selected != null && selected.contains(invoker)) ||(!invoker.isAvailable() && getUrl()!=null && availablecheck)){ try{ Invoker<T> rinvoker = reselect(loadbalance, invocation, invokers, selected, availablecheck); if(rinvoker != null){ invoker = rinvoker; }else{ //看下第一次选的位置,如果不是最后,选+1位置. int index = invokers.indexOf(invoker); try{ //最后在避免碰撞 invoker = index <invokers.size()-1?invokers.get(index+1) :invoker; }catch (Exception e) { logger.warn(e.getMessage()+" may because invokers list dynamic change, ignore.",e); } } }catch (Throwable t){ logger.error("clustor relselect fail reason is :"+t.getMessage() +" if can not slove ,you can set cluster.availablecheck=false in url",t); } } return invoker;}
在源码的“3处”,会用loadbalance负载均衡策略选择出一个提供者,在“4处”,如果被选中的提供者,已经被选择过,则会重新选择一个提供者(调用reselect方法)。
从应用本地提供者列表选择一个提供者(如何选择,查看##负载均衡),
如果调用提供者失败,则重试,重试次数通过retries="2"(默认2次)来配置,重试调用提供者也是从提供者列表中选择一个(如何选择,查看##负载均衡)。如果只有两个提供者,则集群方式变成轮询,即是第1次失败,则第2次重试会调用另一个提供者
重试时指定的提供者是从未调用的提供列表里获取的。
2.快速失败
配置:cluster="failfast"
/** * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。 * <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a> * @author william.liangf */public class FailfastCluster implements Cluster { public final static String NAME = "failfast"; public <T> Invoker<T> join(Directory<T> directory) throws RpcException { return new FailfastClusterInvoker<T>(directory); }}下面看看FailfastClusterInvoker的实现源码:
public class FailfastClusterInvoker<T> extends AbstractClusterInvoker<T>{ public FailfastClusterInvoker(Directory<T> directory) { super(directory); } public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException { checkInvokers(invokers, invocation); //21处,选择一个提供者,如何选择一个提供者,这是由负载均衡部分来实现的,可以看上面源码的doselect方法。 Invoker<T> invoker = select(loadbalance, invocation, invokers, null); try { //22处,这里只调用了一次,成功则返回结果,失败则抛出异常。 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); } }}看看上面的源码,
“21处”,选择一个提供者,如何选择一个提供者,这是由负载均衡部分来实现的,可以看上面源码的doselect方法。
“22处”,这里只调用了一次,成功则返回结果,失败则抛出异常。
3.失败安全
配置:cluster="failsafe"
/** * 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。 * <a href="http://en.wikipedia.org/wiki/Fail-safe">Fail-safe</a> * @author william.liangf */public class FailsafeCluster implements Cluster { public final static String NAME = "failsafe"; public <T> Invoker<T> join(Directory<T> directory) throws RpcException { return new FailsafeClusterInvoker<T>(directory); }}下面看看FailsafeClusterInvoker的实现:
public class FailsafeClusterInvoker<T> extends AbstractClusterInvoker<T>{ private static final Logger logger = LoggerFactory.getLogger(FailsafeClusterInvoker.class); public FailsafeClusterInvoker(Directory<T> directory) { super(directory); } 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) { logger.error("Failsafe ignore exception: " + e.getMessage(), e); //31处,如果异常,则返回结果为空的RpcResult对象。 return new RpcResult(); // ignore } }}看看源码的“31处”,如果调用异常,则返回结果为空的RpcResult对象,这样不至于有异常时影响消费者,这只是当消费者不在意返回结果的情况才使用这种集群容错方式。
4.并行调用
配置:cluster="forking"
/** * 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。 * <a href="http://en.wikipedia.org/wiki/Fork_(topology)">Fork</a> * @author william.liangf */public class ForkingCluster implements Cluster { public final static String NAME = "forking"; public <T> Invoker<T> join(Directory<T> directory) throws RpcException { return new ForkingClusterInvoker<T>(directory); }}下面看看ForkingClusterInvoker的源码实现:
public class ForkingClusterInvoker<T> extends AbstractClusterInvoker<T>{ private final ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory("forking-cluster-timer", true)); public ForkingClusterInvoker(Directory<T> directory) { super(directory); } @SuppressWarnings({ "unchecked", "rawtypes" }) public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException { checkInvokers(invokers, invocation); final List<Invoker<T>> selected; final int forks = getUrl().getParameter(Constants.FORKS_KEY, Constants.DEFAULT_FORKS); final int timeout = getUrl().getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT); if (forks <= 0 || forks >= invokers.size()) { selected = invokers; } else { selected = new ArrayList<Invoker<T>>(); for (int i = 0; i < forks; i++) { //在invoker列表(排除selected)后,如果没有选够,则存在重复循环问题.见select实现. Invoker<T> invoker = select(loadbalance, invocation, invokers, selected); if(!selected.contains(invoker)){//防止重复添加invoker selected.add(invoker); } } } RpcContext.getContext().setInvokers((List)selected); final AtomicInteger count = new AtomicInteger(); final BlockingQueue<Object> ref = new LinkedBlockingQueue<Object>(); //41处,循环调用列表中的每一个提供者 for (final Invoker<T> invoker : selected) { executor.execute(new Runnable() { public void run() { try { Result result = invoker.invoke(invocation); //42处,如果有结果返回则放到LinkedBlockingQueue队列里 ref.offer(result); } catch(Throwable e) { int value = count.incrementAndGet(); if (value >= selected.size()) { //43处,如果调用抛出异常,也相当于有结果返回,也会往队列放一个异常结果。 ref.offer(e); } } } }); } try { //44处,队列阻塞直到有结果,如果是异常,则会抛出异常,有结果则返回。 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); } }}看看上面的源码,
线程池里,会异步地调用每一个提供者,
“41处”,循环调用列表中的每一个提供者,不管调用是有结果还是抛出异常,都会放到队列里,
“42处”,如果有结果返回则放到LinkedBlockingQueue队列里,
“43处”,如果调用抛出异常,也相当于有结果返回,也会往队列放一个异常结果。
“44处”,队列阻塞直到有结果,如果是异常,则会抛出异常,有结果则返回。
5.失败自动恢复
配置: cluster="failback"
/** * 失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作。 * <a href="http://en.wikipedia.org/wiki/Failback">Failback</a> * @author william.liangf */public class FailbackCluster implements Cluster { public final static String NAME = "failback"; public <T> Invoker<T> join(Directory<T> directory) throws RpcException { return new FailbackClusterInvoker<T>(directory); }}下面看看FailbackClusterInvoker的源码实现:
public class FailbackClusterInvoker<T> extends AbstractClusterInvoker<T> { private static final Logger logger = LoggerFactory.getLogger(FailbackClusterInvoker.class); private static final long RETRY_FAILED_PERIOD = 5 * 1000; private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2, new NamedThreadFactory("failback-cluster-timer", true)); private volatile ScheduledFuture<?> retryFuture; private final ConcurrentMap<Invocation, AbstractClusterInvoker<?>> failed = new ConcurrentHashMap<Invocation, AbstractClusterInvoker<?>>(); public FailbackClusterInvoker(Directory<T> directory){ super(directory); } private void addFailed(Invocation invocation, AbstractClusterInvoker<?> router) { //53处,第一次调用addFailed方法时,retryFutrue为null,会启动定时器scheduledExecutorService if (retryFuture == null) { synchronized (this) { if (retryFuture == null) { //54处,定时器5000毫秒执行一次retryFailed方法,retryFailed方法会循环failed Map对象里的失败调用,再次调用。 retryFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() { public void run() { // 收集统计信息 try { retryFailed(); } catch (Throwable t) { // 防御性容错 logger.error("Unexpected error occur at collect statistic", t); } } }, RETRY_FAILED_PERIOD, RETRY_FAILED_PERIOD, TimeUnit.MILLISECONDS); } } } //55处,将调用放入Map对象failed中。 failed.put(invocation, router); } void retryFailed() { if (failed.size() == 0) { return; } for (Map.Entry<Invocation, AbstractClusterInvoker<?>> entry : new HashMap<Invocation, AbstractClusterInvoker<?>>( failed).entrySet()) { Invocation invocation = entry.getKey(); Invoker<?> invoker = entry.getValue(); try { invoker.invoke(invocation); failed.remove(invocation); } catch (Throwable e) { logger.error("Failed retry to invoke method " + invocation.getMethodName() + ", waiting again.", e); } } } protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException { try { checkInvokers(invokers, invocation); Invoker<T> invoker = select(loadbalance, invocation, invokers, null); //51处,会根据负载均衡策略得到一个提供者,调用提供者。 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); //52处,如果调用异常,则加入异常Map中,以备定时器定时重新调用提供者。 addFailed(invocation, this); return new RpcResult(); // ignore } }}从FailbackClusterInvoker的源码中,
“51处”,会根据负载均衡策略得到一个提供者,调用提供者。如果调用失败异常了,会执行“52处”,如果调用异常,则加入异常Map中,以备定时器定时重新调用提供者。
调用异常进入方法addFailed,在“53处”,第一次调用addFailed方法时,retryFutrue为null,会启动定时器scheduledExecutorService,接着在源码“54处”,定时器5000毫秒执行一次retryFailed方法,retryFailed方法会循环failed Map对象里的失败调用,再次调用。在“55处”,就会将调用放入Map对象failed中。
0 0
- (三)集群容错
- 弹性分布式数据集:一种对内存集群计算的容错抽象(三)
- Dubbo服务集群容错配置(四)
- Dubbo集群容错
- Dubbo集群容错
- dubbo集群容错模式
- dubbo——集群容错
- Dubbo学习(四):集群容错
- Spark集群容错场景介绍
- Dubbo服务集群容错配置
- Dubbo集群容错机制解析
- Redis(三)集群
- dubbo 学习(4) 集群容错模式和负载均衡模式
- 分布式对象存储Ambry(2)基本使用API与集群容错测试
- 弹性分布式数据集:一种对内存集群计算的容错抽象(一)
- 弹性分布式数据集:一种对内存集群计算的容错抽象(二)
- Redis3.0 集群模式 容错 简析
- dubbo工作原理,集群容错,负载均衡
- vs 2015 安装ionic后启动页面没有显示
- dns备忘录
- 【Office】使用数字证书进行数字签名
- MFC中如何画带实心箭头的直线
- Linux关机命令详解
- (三)集群容错
- 数据集 —— ground truth 数据集
- 页面动态化的基础 —— Tangram
- 三分学习(解决凸函数or凹函数的极值)
- MFC轻松实现对话框标题更改
- Hibernate映射集合属性3__排序用的sort属性
- 566. Reshape the Matrix
- 欢迎使用CSDN-markdown编辑器
- Minimum number of steps java