cluster的基类:AbstractClusterInvoker
/**
执行
1.获取到集群中对应的所有提供者url
2.获取到负载均衡的方式
3.如果是异步的话,参数加上invocation id
4.执行doInvoke交给对应的容错机制来处理
**/
public Result invoke(final Invocation invocation) throws RpcException {
LoadBalance loadbalance;
//获取到所有的invoker(集群中的所有的提供者url)
List<Invoker<T>> invokers = list(invocation);
//获取负载均衡
if (invokers != null && invokers.size() > 0) {
//如果是集群,那么就选择第一个url中的参数loadbalance
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).
getExtension(invokers.get(0).getUrl()
.getMethodParameter(
invocation.getMethodName(),"loadbalance", "random"));
} else {
//选择默认random
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class)
.getExtension(Constants."random");
}
//幂等操作:异步操作默认添加invocation id
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
/**
选择哪一个调用者
1. 如果设置了sticky黏性,在第一次选择过提供者后保存下来,以便下次调用,只要提供者没有挂
掉,还是该提供者(保证每次调用的都是同一个提供者)
2. 如果就一个提供者直接返回;有两个提供者,轮询。
3. 使用loadbalance选择invoker.
4. 如果 selected中包含(优先判断) 或者 不可用&&availablecheck=true 则重试.
5. 重试的机制是
先从非select中选,没有的话,再从select中选择
@param availablecheck 如果设置true,在选择的时候先选invoker.available == true
@param selected 已选过的invoker
*/
protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation,
List<Invoker<T>> invokers, List<Invoker<T>> selected) {
String methodName = invocation == null ? "" : invocation.getMethodName();
//处理服务的黏性问题,尽可能让客户端总是同一提供者发起调用,
//除非该提供者挂了,再连另一台
boolean sticky = invokers.get(0).getUrl().getMethodParameter(methodName,
"sticky", false) ;
{
if ( stickyInvoker != null && !invokers.contains(stickyInvoker) ){
stickyInvoker = null;
}
//如果提供者没有挂掉,那么还是这个提供者
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.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);
}
//使用负载均衡,来获取到Invoker
Invoker<T> invoker = loadbalance.select(invokers, getUrl(), invocation);
//如果 selected中包含(优先判断) 或者 不可用&&availablecheck=true 则重试.
if( (selected != null && selected.contains(invoker))
||(!invoker.isAvailable() && getUrl()!=null && availablecheck)){
//重新选择invoker
Invoker<T> rinvoker = reselect(loadbalance, invocation, invokers,
selected, availablecheck);
if(rinvoker != null){
invoker = rinvoker;
}else{
//看下第一次选的位置,如果不是最后,选+1位置.
int index = invokers.indexOf(invoker);
//最后在避免碰撞
invoker = index <invokers.size()-1?invokers.get(index+1)
:invoker;
}
}
return invoker;
}
/**
* 重选,先从非selected的列表中选择,没有在从selected列表中选择.
*/
private Invoker<T> reselect(LoadBalance loadbalance,Invocation invocation,
List<Invoker<T>> invokers, List<Invoker<T>> selected ,
boolean availablecheck){
//预先分配一个,这个列表是一定会用到的.
List<Invoker<T>> reselectInvokers = new ArrayList<Invoker<T>>(
invokers.size()>1?(invokers.size()-1):invokers.size());
//先从非select中选
if( availablecheck ){
//选isAvailable 的非select
for(Invoker<T> invoker : invokers){
if(invoker.isAvailable()){
if(selected ==null || !selected.contains(invoker)){
reselectInvokers.add(invoker);
}
}
}
}else{ //选全部非select
for(Invoker<T> invoker : invokers){
if(selected ==null || !selected.contains(invoker)){
reselectInvokers.add(invoker);
}
}
}
if(reselectInvokers.size()>0){
return loadbalance.select(reselectInvokers, getUrl(), invocation);
}
//最后从select中选可用的.
{
if(selected != null){
for(Invoker<T> invoker : selected){
if((invoker.isAvailable()) //优先选available
&& !reselectInvokers.contains(invoker)){
reselectInvokers.add(invoker);
}
}
}
if(reselectInvokers.size()>0){
return loadbalance.select(reselectInvokers, getUrl(), invocation);
}
}
return null;
}
获取集群中的提供者url地址
protected List<Invoker<T>> list(Invocation invocation) {
List<Invoker<T>> invokers = directory.list(invocation);
return invokers;
}
根据AbstractDirectory来获取集群的提供者
public abstract class AbstractDirectory<T> implements Directory<T> {
//获取集群中的提供者url
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
List<Invoker<T>> invokers = doList(invocation);
List<Router> localRouters = this.routers;
if (localRouters != null && localRouters.size() > 0) {
for (Router router: localRouters){
if (router.getUrl() == null || router.getUrl().getParameter(
"runtime", true)) {
invokers = router.route(invokers,
getConsumerUrl(), invocation);
}
}
}
return invokers;
}
}
AbstractDirectory有两个子类RegistryDirectory和StaticDirectory
public class RegistryDirectory<T> extends AbstractDirectory<T>
implements NotifyListener {
/**
获取invoker
从本地缓存中获取对应的invoker列表
当zookeeper上的providers节点发生改变时,会通知消息,那么这时就
会刷新本地的Invoker列表
**/
public List<Invoker<T>> doList(Invocation invocation) {
if (forbidden) {
throw new RpcException("");
}
List<Invoker<T>> invokers = null;
// 当zookeeper上的providers节点发生改变时,会通知消息,那么这时就
//会刷新本地的Invoker
Map<String, List<Invoker<T>>> localMethodInvokerMap =
this.methodInvokerMap;
if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
//获取方法名称和参数
String methodName = RpcUtils.getMethodName(invocation);
Object[] args = RpcUtils.getArguments(invocation);
if(args != null && args.length > 0 && args[0] != null
&& (args[0] instanceof String || args[0].getClass().isEnum())) {
invokers = localMethodInvokerMap.get(methodName + "."
+ args[0]); // 可根据第一个参数枚举路由
}
if(invokers == null) {
invokers = localMethodInvokerMap.get(methodName);
}
if(invokers == null) {
invokers = localMethodInvokerMap.get(Constants.ANY_VALUE);
}
if(invokers == null) {
Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.
values().iterator();
if (iterator.hasNext()) {
invokers = iterator.next();
}
}
}
return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
}
}
RpcUtils中,对于异步操作来说,参数加上invocationId的序号
private static final AtomicLong INVOKE_ID = new AtomicLong(0);
/**
* 幂等操作:异步操作默认添加invocation id
* @param url
* @param inv
*/
public static void attachInvocationIdIfAsync(URL url, Invocation inv){
if (isAttachInvocationId(url, inv) && getInvocationId(inv) == null &&
inv instanceof RpcInvocation)
((RpcInvocation)inv).setAttachment("id",
String.valueOf(INVOKE_ID.getAndIncrement()));
}
//判断是否是异步
private static boolean isAttachInvocationId(URL url , Invocation invocation){
String value = url.getMethodParameter(invocation.getMethodName(),
"invocationid.autoattach");
if ( value == null ) {
//异步操作默认添加invocationid
return isAsync(url,invocation) ;
} else if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {
//设置为添加,则一定添加
return true;
} else {
//value为false时,不添加
return false;
}
}
负载均衡
<dubbo:service
interface="com.test.dubbo.order.api.IOrderService"
ref="orderService" loadbalance=""/>
在集群负载均衡时,Dubbo提供了多种均衡策略,缺省为random随机调用。可以自行扩展负载均衡策略
- Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重 - RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。 - LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。 - ConsistentHash LoadBalance
一致性Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
AbstractLoadBalance
public abstract class AbstractLoadBalance implements LoadBalance {
/**
如果是一个,直接返回
**/
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url,
Invocation invocation) {
if (invokers == null || invokers.size() == 0)
return null;
if (invokers.size() == 1)
return invokers.get(0);
return doSelect(invokers, url, invocation);
}
}
//获取权重
protected int getWeight(Invoker<?> invoker, Invocation invocation) {
int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(),
"weight", 100);
if (weight > 0) {
long timestamp = invoker.getUrl().getParameter("timestamp", 0L);
if (timestamp > 0L) {
int uptime = (int) (System.currentTimeMillis() - timestamp);
int warmup = invoker.getUrl().getParameter("warmup", 10 * 60 * 1000);
if (uptime > 0 && uptime < warmup) {
weight = calculateWarmupWeight(uptime, warmup, weight);
}
}
}
return weight;
}
//计算权重
static int calculateWarmupWeight(int uptime, int warmup, int weight) {
int ww = (int) ( (float) uptime / ( (float) warmup / (float) weight ) );
return ww < 1 ? 1 : (ww > weight ? weight : ww);
}
- RandomLoadBalance
根据权重,如果每个的权重不一样,那么就随机总权重,否则随机获取
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url,
Invocation invocation) {
int length = invokers.size(); // 总个数
int totalWeight = 0; // 总权重
boolean sameWeight = true; // 权重是否都一样
for (int i = 0; i < length; i++) {
int weight = getWeight(invokers.get(i), invocation);
totalWeight += weight; // 累计总权重
if (sameWeight && i > 0
&& weight != getWeight(invokers.get(i - 1), invocation)) {
sameWeight = false; // 计算所有权重是否一样
}
}
if (totalWeight > 0 && !sameWeight) {
// 如果权重不相同且权重大于0则按总权重数随机
int offset = random.nextInt(totalWeight);
// 并确定随机值落在哪个片断上
for (int i = 0; i < length; i++) {
offset -= getWeight(invokers.get(i), invocation);
if (offset < 0) {
return invokers.get(i);
}
}
}
// 如果权重相同或权重为0则均等随机
return invokers.get(random.nextInt(length));
}
- RoundRobinLoadBalance
轮循,按公约后的权重设置轮循比率。
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url,
Invocation invocation) {
//service+method
String key = invokers.get(0).getUrl().getServiceKey() + "." +
invocation.getMethodName();
int length = invokers.size(); // 总个数
int maxWeight = 0; // 最大权重
int minWeight = Integer.MAX_VALUE; // 最小权重
//取出最大和最小的权重
for (int i = 0; i < length; i++) {
int weight = getWeight(invokers.get(i), invocation);
maxWeight = Math.max(maxWeight, weight); // 累计最大权重
minWeight = Math.min(minWeight, weight); // 累计最小权重
}
if (maxWeight > 0 && minWeight < maxWeight) { // 权重不一样
AtomicPositiveInteger weightSequence = weightSequences.get(key);
if (weightSequence == null) {
weightSequences.putIfAbsent(key, new AtomicPositiveInteger());
weightSequence = weightSequences.get(key);
}
int currentWeight = weightSequence.getAndIncrement() % maxWeight;
List<Invoker<T>> weightInvokers = new ArrayList<Invoker<T>>();
// 筛选权重大于当前权重基数的Invoker
for (Invoker<T> invoker : invokers) {
if (getWeight(invoker, invocation) > currentWeight) {
weightInvokers.add(invoker);
}
}
int weightLength = weightInvokers.size();
if (weightLength == 1) {
return weightInvokers.get(0);
} else if (weightLength > 1) {
invokers = weightInvokers;
length = invokers.size();
}
}
AtomicPositiveInteger sequence = sequences.get(key);
if (sequence == null) {
sequences.putIfAbsent(key, new AtomicPositiveInteger());
sequence = sequences.get(key);
}
// 取模轮循
return invokers.get(sequence.getAndIncrement() % length);
}
- LeastActiveLoadBalance
最少活跃调用数(调用前后计数差。),相同活跃数的随机。
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url,
Invocation invocation) {
int length = invokers.size(); // 总个数
int leastActive = -1; // 最小的活跃数
int leastCount = 0; // 相同最小活跃数的个数
int[] leastIndexs = new int[length]; // 相同最小活跃数的下标
int totalWeight = 0; // 总权重
int firstWeight = 0; // 第一个权重,用于于计算是否相同
boolean sameWeight = true; // 是否所有权重相同
for (int i = 0; i < length; i++) {
Invoker<T> invoker = invokers.get(i);
// 活跃数
int active = RpcStatus.getStatus(invoker.getUrl(),
invocation.getMethodName()).getActive();
// 权重
int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(),
"weight", 100);
if (leastActive == -1 || active < leastActive) { // 发现更小的活跃数,重新开始
leastActive = active; // 记录最小活跃数
leastCount = 1; // 重新统计相同最小活跃数的个数
leastIndexs[0] = i; // 重新记录最小活跃数下标
totalWeight = weight; // 重新累计总权重
firstWeight = weight; // 记录第一个权重
sameWeight = true; // 还原权重相同标识
} else if (active == leastActive) { // 累计相同最小的活跃数
leastIndexs[leastCount ++] = i; // 累计相同最小活跃数下标
totalWeight += weight; // 累计总权重
// 判断所有权重是否一样
if (sameWeight && i > 0
&& weight != firstWeight) {
sameWeight = false;
}
}
}
if (leastCount == 1) {
// 如果只有一个最小则直接返回
return invokers.get(leastIndexs[0]);
}
if (! sameWeight && totalWeight > 0) {
// 如果权重不相同且权重大于0则按总权重数随机
int offsetWeight = random.nextInt(totalWeight);
// 并确定随机值落在哪个片断上
for (int i = 0; i < leastCount; i++) {
int leastIndex = leastIndexs[i];
offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
if (offsetWeight <= 0)
return invokers.get(leastIndex);
}
}
// 如果权重相同或权重为0则均等随机
return invokers.get(leastIndexs[random.nextInt(leastCount)]);
}
- ConsistentHashLoadBalance
待研究
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url,
Invocation invocation) {
//service+method
String key = invokers.get(0).getUrl().getServiceKey() + "." +
invocation.getMethodName();
//获取唯一hashCode
int identityHashCode = System.identityHashCode(invokers);
ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>)
selectors.get(key);
//初始化时或者当hashcode不一致时(当提供者增加或者删除时)
if (selector == null ||
selector.getIdentityHashCode() != identityHashCode) {
selectors.put(key, new ConsistentHashSelector<T>(invokers,
invocation.getMethodName(), identityHashCode));
selector = (ConsistentHashSelector<T>) selectors.get(key);
}
return selector.select(invocation);
}
private static final class ConsistentHashSelector<T> {
//虚拟invoker
private final TreeMap<Long, Invoker<T>> virtualInvokers;
//节点数
private final int replicaNumber;
private final int identityHashCode;
private final int[] argumentIndex;
public ConsistentHashSelector(List<Invoker<T>> invokers, String methodName,
int identityHashCode) {
this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
this.identityHashCode = System.identityHashCode(invokers);
URL url = invokers.get(0).getUrl();
this.replicaNumber = url.getMethodParameter(
methodName, "hash.nodes", 160);
String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.
getMethodParameter(methodName, "hash.arguments", "0"));
argumentIndex = new int[index.length];
for (int i = 0; i < index.length; i ++) {
argumentIndex[i] = Integer.parseInt(index[i]);
}
for (Invoker<T> invoker : invokers) {
for (int i = 0; i < replicaNumber / 4; i++) {
byte[] digest = md5(invoker.getUrl().toFullString() + i);
for (int h = 0; h < 4; h++) {
long m = hash(digest, h);
virtualInvokers.put(m, invoker);
}
}
}
}
public int getIdentityHashCode() {
return identityHashCode;
}
public Invoker<T> select(Invocation invocation) {
String key = toKey(invocation.getArguments());
byte[] digest = md5(key);
Invoker<T> invoker = sekectForKey(hash(digest, 0));
return invoker;
}
private String toKey(Object[] args) {
StringBuilder buf = new StringBuilder();
for (int i : argumentIndex) {
if (i >= 0 && i < args.length) {
buf.append(args[i]);
}
}
return buf.toString();
}
private Invoker<T> sekectForKey(long hash) {
Invoker<T> invoker;
Long key = hash;
if (!virtualInvokers.containsKey(key)) {
SortedMap<Long, Invoker<T>> tailMap = virtualInvokers.tailMap(key);
if (tailMap.isEmpty()) {
key = virtualInvokers.firstKey();
} else {
key = tailMap.firstKey();
}
}
invoker = virtualInvokers.get(key);
return invoker;
}
//计算hash值
private long hash(byte[] digest, int number) {
return (((long) (digest[3 + number * 4] & 0xFF) << 24)
| ((long) (digest[2 + number * 4] & 0xFF) << 16)
| ((long) (digest[1 + number * 4] & 0xFF) << 8)
| (digest[0 + number * 4] & 0xFF))
& 0xFFFFFFFFL;
}
//加密
private byte[] md5(String value) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e.getMessage(), e);
}
md5.reset();
byte[] bytes = null;
try {
bytes = value.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e.getMessage(), e);
}
md5.update(bytes);
return md5.digest();
}
}
cluster
在Reference中,返回的Invoker是根据对应的容错机制生成的Invoker
<dubbo:reference id="testService"
interface="com.test.ITestService"
cluster="failfast"/>
- failover cluster 失败的时候自动切换并重试其他服务器。 通过retries=2。 来设置重试次数(默认)
- failfast cluster 快速失败,只发起一次调用 ; 写操作。比如新增记录的时候, 非幂等请求
- failsafe cluster 失败安全。 出现异常时,直接忽略异常 – 写日志
- failback cluster 失败自动恢复。 后台记录失败请求,定时重发
- forking cluster 并行调用多个服务器,只要一个成功就返回。 只能应用在读请求
- broadcast cluster 广播调用所有提供者,逐个调用。.3其中一台报错就会返回异常
@SPI(FailoverCluster.NAME)
public interface Cluster {
@Adaptive
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
}
FailoverCluster
当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。 默认失败重试次数是2,加上正常的那一次,调用总数是3次。
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) t {
List<Invoker<T>> copyinvokers = invokers;
//检查属性
//默认次数是2次+1
int len = getUrl().getMethodParameter(invocation.getMethodName(),
"retries", 2) + 1;
if (len <= 0) {
len = 1;
}
// retry loop.
RpcException le = null; // last exception.
List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size());
Set<String> providers = new HashSet<String>(len);
//循环重试次数
for (int i = 0; i < len; i++) {
//重试时,进行重新选择,避免重试时invoker列表已发生变化.
//注意:如果列表发生了变化,那么invoked判断会失效,因为invoker实例已经改变
if (i > 0) {
//检查是否已经注销
checkWheatherDestoried();
//重新获取invoke列表(避免重试时invoker列表已发生变化.)
copyinvokers = list(invocation);
//重新检查一下
checkInvokers(copyinvokers, invocation);
}
//选择对应的invoker
Invoker<T> invoker = select(loadbalance, invocation,
copyinvokers, invoked);
invoked.add(invoker);
RpcContext.getContext().setInvokers((List)invoked);
try {
//执行
Result result = invoker.invoke(invocation);
return result;
} finally {
providers.add(invoker.getUrl().getAddress());
}
}
throw new RpcException("");
}
}
- 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);
//选择invoker,执行,出现异常直接抛出
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
try {
return invoker.invoke(invocation);
} catch (Throwable e) {
throw new RpcException("");
}
}
}
- BroadcastClusterInvoker
广播调用所有提供者,逐个调用。其中一台报错就会返回异常
public Result doInvoke(final Invocation invocation, List<Invoker<T>>
invokers, LoadBalance loadbalance) throws RpcException {
checkInvokers(invokers, invocation);
RpcContext.getContext().setInvokers((List)invokers);
RpcException exception = null;
Result result = null;
//循环调用所有的提供者,只要出现异常,就抛出
for (Invoker<T> invoker: invokers) {
try {
result = invoker.invoke(invocation);
} catch (RpcException e) {
exception = e;
logger.warn(e.getMessage(), e);
} catch (Throwable e) {
exception = new RpcException(e.getMessage(), e);
logger.warn(e.getMessage(), e);
}
}
if (exception != null) {
throw exception;
}
return result;
}
- FailbackClusterInvoker
失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作。
//忽视异常,放入到列表中,定时等待重试
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);
return invoker.invoke(invocation);
} catch (Throwable e) {
addFailed(invocation, this);
return new RpcResult();
}
}
//启动定时,每隔5秒调用
private void addFailed(Invocation invocation, AbstractClusterInvoker<?> router) {
if (retryFuture == null) {
synchronized (this) {
if (retryFuture == null) {
retryFuture =
scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
public void run() {
// 收集统计信息
try {
retryFailed();
} catch (Throwable t) { // 防御性容错
}
}
}, 5 * 1000, 5 * 1000, TimeUnit.MILLISECONDS);
}
}
}
failed.put(invocation, router);
}
void retryFailed() {
if (failed.size() == 0) {
return;
}
//循环调用出现异常的invoker
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) {
}
}
}
- FailsafeClusterInvoker
失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。
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) {
return new RpcResult(); // ignore
}
}
- ForkingClusterInvoker
并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。
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("forks",2);
final int timeout = getUrl().getParameter("timeout", 1000);
if (forks <= 0 || forks >= invokers.size()) {
selected = invokers;
} else {
selected = new ArrayList<Invoker<T>>();
//开启线程
for (int i = 0; i < forks; i++) {
//在invoker列表(排除selected)后,如果没有选够,则存在重复循环问题.
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>();
for (final Invoker<T> invoker : selected) {
executor.execute(new Runnable() {
public void run() {
try {
Result result = invoker.invoke(invocation);
//当成功了,即唤醒队列
ref.offer(result);
} catch(Throwable e) {
int value = count.incrementAndGet();
//当出现异常时,但是次数是selected,才唤醒
if (value >= selected.size()) {
ref.offer(e);
}
}
}
});
}
try {
Object ret = ref.poll(timeout, TimeUnit.MILLISECONDS);
if (ret instanceof Throwable) {
Throwable e = (Throwable) ret;
throw new RpcException("");
}
return (Result) ret;
} catch (InterruptedException e) {
throw new RpcException("");
}
}