本文基于dubbo 2.7.5版本代码
集群容错八大Invoker实现类详解
AbstractClusterInvoker子类只实现了doInvoke方法,所以后面的文章只介绍doInvoke方法。
doInvoke方法定义如下:
public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance)
入参invokers是远程服务提供者集合,也就是Directory的list方法返回值,loadbalance是负载均衡算法实现对象,invocation包含了访问远程服务的信息,比如要访问的服务名、服务方法等。
FailoverClusterInvoker
failover翻译为故障转移,所以该类如果第一次调用失败,那么会第二次尝试,第二次尝试会访问其他服务提供者。该类是默认的集群容错策略。
这个类用到了参数retries,表示重试次数,默认是2,所以访问一个服务,最多会调用retries+1次,如果retries+1次都失败,才会返回失败。该参数可在@Reference和@Service中配置。
第一次调用时,先调用父类的select方法选出一个远程服务提供者,然后调用该提供者,如果失败,那么进入重试流程。重试流程如下:
FailfastClusterInvoker
该类的策略与java里面多线程访问集合的fast-fail是一样的。调用父类的select方法选出一个服务提供者,然后访问该提供者,如果失败了,直接向调用者抛出异常结束。
FailsafeClusterInvoker
安全失败集群调用器。调用的过程与FailfastClusterInvoker是类似的,不同的是当访问失败时,FailsafeClusterInvoker不直接抛出异常,而是返回一个值为null的AsyncRpcResult对象。也就是说当访问远程服务失败时,调用方不会收到异常,而是收到一个null的返回值。
AvailableClusterInvoker
可用集群调用器。前面提到doInvoke的入参有远程服务提供者的列表invokers。AvailableClusterInvoker遍历invokers,当遍历到第一个服务可用的提供者时,便访问该提供者,成功返回结果,如果访问时失败抛出异常终止遍历。
判断提供者服务是否可用,是检查Invoker对象的isAvailable方法,该方法的判断逻辑后面的文章再介绍。
FailbackClusterInvoker
该类如果访问远程服务成功,将返回值返回调用者。
如果访问失败,会向调用者返回null,然后调用addFailed方法,创建定时任务再次执行服务调用。
该容错策略适用于通知类型服务,调用者不需要服务的返回值。该类涉及到两个参数:retries和failbacktasks。
- retries:表示如果失败了,定时任务的重试次数,默认3;
- failbacktasks:表示定时器的最大挂起任务数,默认100。
定时器使用的是时间轮算法HashedWheelTimer,在该类中,定时器每1s执行一个任务,关于该算法以后的文章在做介绍。
定时任务是RetryTimerTask,该定时任务每次执行时,直接访问父类select方法筛选出的服务提供者,如果访问失败,则重新将该任务加入到定时器中,直到访问成功或者超过retries或者failbacktasks的限制。每次加入到定时器中的任务都是延迟5s后执行。
ForkingClusterInvoker
该策略是同时对多个服务提供者进行调用,只要有一个服务调用成功即将结果返回调用者。通常用于要求实时的操作,但需要浪费更多的服务资源。使用该策略,需要做好服务幂等性以及返回值的处理。
参数forks表示要同时调用多少个服务提供者;
参数timeout表示在第一个服务调用成功或者失败前的超时时间,单位毫秒。
如果设置的forks满足下述条件
forks <= 0 || forks >= invokers.size()//invokers是doInvoke的入参
那么认为所有的服务提供者都要同时调用。否则就从invokers中筛选出forks个提供者调用。筛选规则也是使用父类的select方法。
每调用一个服务提供者作为一个任务加入到ExecutorService线程池,每个任务对应一个线程,线程池的线程数可以认为是无限的。服务提供者返回结果后,将结果放到阻塞队列BlockingQueue中。ForkingClusterInvoker等待该阻塞队列,最大等待时间为参数timeout设定值,一旦队列中有成功的返回结果或者异常,就将结果或异常返回。
MergeableClusterInvoker
该集群容错策略是对多个服务端返回结果合并。
在以前的版本里面,调用服务提供者是使用线程池异步调用的,在2.7.5版本里面改成了同步调用,但是线程池字段保留了下来,不清楚在以后的版本里面是否还会改为异步。
合并结果,需要使用参数merger。如果没有配置merger,那么不使用结果合并功能,调用第一个可用的服务提供者后返回。参见如下代码:
if (ConfigUtils.isEmpty(merger)) {
for (final Invoker<T> invoker : invokers) {
if (invoker.isAvailable()) {
try {
return invoker.invoke(invocation);
} catch (RpcException e) {
...//代码删减了
}
}
}
return invokers.iterator().next().invoke(invocation);
}
merger可以配置的值有true、default、Merger对象名、以"."开头的方法名。下面先看一下该策略的整体流程,之后在对各个merger参数的配置详细介绍。
下面介绍一下merger各个值的含义。
- merger=true或者default,表示使用默认的Merger对象合并结果,因为Merger接口的@SPI未配置默认值,所以在2.7.5版本dubbo无法使用默认Merger对象。
- merger=Merger对象名,dubbo使用SPI查找与名字一致的Merger对象。
- merger=以"."开头,后面是方法名。这里的方法名是远程服务方法的返回类中的方法,得到每个服务提供者的返回对象后,遍历每个返回对象,调用该方法。下面代码里面的resultList保存了所有的返回对象,method是Method对象,也就是merger指定的方法,result是最后返回调用方的结果。
if (method.getReturnType() != void.class
&& method.getReturnType().isAssignableFrom(result.getClass())) {
for (Result r : resultList) {
result = method.invoke(result, r.getValue());
}
} else {
for (Result r : resultList) {
method.invoke(result, r.getValue());
}
}
对于Merger接口的实现之后文章介绍。
BroadcastClusterInvoker
该策略是遍历调用每个远程服务提供者,遍历的集合是doInvoke的入参invokers,也就是所有的远程服务提供者。调用是同步的,如果提供者比较多,会非常耗时。在遍历的过程中,有异常不会中断遍历,将异常记录,遍历完后抛出异常。
该类通常用于通知所有提供者更新缓存或日志等本地资源信息。
引用自官网:http://dubbo.apache.org/zh-cn/docs/source_code_guide/cluster.html