dubbo集群容错机制
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 Failover 重试。
各节点关系:
这里的 Invoker 是 Provider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息
Directory 代表多个 Invoker,可以把它看成 List ,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更
Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个
Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等
LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选
Dubbo中具体实现失败重试的是FailoverClusterInvoker类,这里我们看下具体实现,主要看下doInvoke代码:
public Result doInvoke(Invocation invocation,final List<Invoker<T>> invokers,LoadBalance loadbalance) throws RpcException{
// (1) 所有服务提供者
List<Invoker<T>> copyinvokers = invokers;
checkInvokers(copyinvokers,invocation);
// (2)获取重试次数
int len = getUrl().getMethodParameter(invocation.getMethodName(),Constants.RETRIES_KEY,Constants.DEFAULT_RETRIES) + 1;
if(len <= 0){
len = 1;
}
// (3)使用循环,失败重试
RpcException le = null; // last exception
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size());
Set<String> providers = new HashSet<String>();
for(int i=0;i<len;i++){
// 重试时,进行重新选择,避免重试时invoker列表已发生变化
// 注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变
if(i > 0){
// (3.1)
checkWhetherDestroyed(); // 如果当前实例已经被销毁,则抛出异常
// (3.2) 重新获取所有服务提供者
copyinvokers = list(invocation);
// (3.3) 重新检查一下
checkInvokers(copyinvokers,invocation);
}
// (3.4) 选择负载均衡策略
Invoker<T> invoker = select(loadbalance,invocation,copyinvokers,invoked);
invoked.add(invoker);
RpcContext.getContext().setInvokers((List)invoked);
// (3.5) 具体发起远程调用
try{
Result result = invoker.invoke(invocation);
if(le != null && logger.isWarnEnabled()){
...
}
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("抛出异常...");
}
- 如上代码(2)从url参数里面获取设置的重试次数,如果用户没有设置则取默认的值,默认是重试2,这里需要注意的是代码(2)是获取配置重试次数又+1了。这说明 总共调用次数=重试次数+1 (1是正常调用)。
- 代码(3)循环重复试用,如果第一次调用成功则直接跳出循环返回,否则循环重试。第一次调用时不会走代码(3.1)(3.2)(3.3)。如果第一次调用出现异常,则会循环,这时候i=1,所以会执行代码(3.1)检查是否有线程调用了当前ReferenceConfig的destroy()方法,销毁了当前消费者。如果当前消费者实例已经被消费,那么重试就没有意义了,所以会抛出RpcException异常。
- 如果当前消费者实例没被销毁,则执行代码(3.2)重新获取当前服务提供者列表,这是因为从第一次调开始到线程可能提供者列表已经变化了,获取列表后,然后执行(3.2)又一次进行了校验。校验通过则执行(3.4),根据负载均衡策略选择一个服务提供者,再次尝试调用。负载均衡策略的选择下节会讲解。
5 重试模式及其特点
Failover Cluster(默认)
当服务消费方调用服务提供者失败后自动切换到其他服务提供者服务器进行重试。这通常用于读操作或者具有幂等的写操作,需要注意的是重试会带来更长延迟。可通过 retries=”2” 来设置重试次数(不含第一次)。
接口级别配置重试次数方法
失败自动切换,当出现失败,重试其它服务器 \[1\]。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
重试次数配置如下:
<dubbo:service retries="2" />
或
<dubbo:reference retries="2" />
或
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错 \[2\]。通常用于通知所有提供者更新缓存或日志等本地资源信息。
6 集群模式配置
按照以下示例在服务提供方和消费方配置集群模式
<dubbo:service cluster="failsafe" />
或
<dubbo:reference cluster="failsafe" />
还没有评论,来说两句吧...