Ribbon的版本是2.3.0.release.
1.BestAvailableRule
图1
ClientConfigEnabledRoundRobinRule如下所示,定义了一个类属性RoundRobinRule,choose方法中调用RoundRobinRule进行选择,所以这里面的是轮循算法。
List-1.1
public class ClientConfigEnabledRoundRobinRule extends AbstractLoadBalancerRule {
RoundRobinRule roundRobinRule = new RoundRobinRule();
public ClientConfigEnabledRoundRobinRule() {
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
this.roundRobinRule = new RoundRobinRule();
}
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
this.roundRobinRule.setLoadBalancer(lb);
}
public Server choose(Object key) {
if (this.roundRobinRule != null) {
return this.roundRobinRule.choose(key);
} else {
throw new IllegalArgumentException("This class has not been initialized with the RoundRobinRule class");
}
}
}
BestAvailableRule继承了ClientConfigEnabledRoundRobinRule,内部实现如下List-1.2,遍历所有的服务提供者,选择并发量最小的那个服务。
List-1.2
private LoadBalancerStats loadBalancerStats;
public Server choose(Object key) {
if (this.loadBalancerStats == null) {
return super.choose(key);
} else {
List<Server> serverList = this.getLoadBalancer().getAllServers();
int minimalConcurrentConnections = 2147483647;
long currentTime = System.currentTimeMillis();
Server chosen = null;
Iterator var7 = serverList.iterator();
while(var7.hasNext()) {
Server server = (Server)var7.next();
ServerStats serverStats = this.loadBalancerStats.getSingleServerStat(server);
if (!serverStats.isCircuitBreakerTripped(currentTime)) {
int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
if (concurrentConnections < minimalConcurrentConnections) {
minimalConcurrentConnections = concurrentConnections;
chosen = server;
}
}
}
if (chosen == null) {
return super.choose(key);
} else {
return chosen;
}
}
}
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
if (lb instanceof AbstractLoadBalancer) {
this.loadBalancerStats = ((AbstractLoadBalancer)lb).getLoadBalancerStats();
}
}
choose方法重新了父类中的choose方法,
- 获取服务列表,遍历服务
- 通过ServerStats获取当前服务实例的并发连接数,如下List-3所示,并发连接数不是0,且当前时间与上次有效更改时间间隔在范围内,则返回当前并发连接数。
- 遍历所有的服务提供者后,如果得到的server是null,则调用父类的choose方法,用RoundRobin算法进行选择。
List-1.3
public int getActiveRequestsCount(long currentTime) {
int count = this.activeRequestsCount.get();
if (count == 0) {
return 0;
} else if (currentTime - this.lastActiveRequestsCountChangeTimestamp <= (long)(activeRequestsCountTimeout.get() * 1000) && count >= 0) {
return count;
} else {
this.activeRequestsCount.set(0);
return 0;
}
}
2.RetryRule
图2
RetryRule的实现比较简单,基于RoundRobinRule,如下List-2.1所示
List-2.1
public class RetryRule extends AbstractLoadBalancerRule {
IRule subRule = new RoundRobinRule();
long maxRetryMillis = 500L;
...
public Server choose(ILoadBalancer lb, Object key) {
long requestTime = System.currentTimeMillis();
long deadline = requestTime + this.maxRetryMillis;
Server answer = null;
answer = this.subRule.choose(key);
if ((answer == null || !answer.isAlive()) && System.currentTimeMillis() < deadline) {
InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis());
while(!Thread.interrupted()) {
answer = this.subRule.choose(key);
if (answer != null && answer.isAlive() || System.currentTimeMillis() >= deadline) {
break;
}
Thread.yield();
}
task.cancel();
}
return answer != null && answer.isAlive() ? answer : null;
}
...
RetryRule中choose(Object key)调用choose(ILoadBalancer lb, Object key),
- 当前时间加上maxRetryMillis得到deadline,即截止时间
- 用subRule获取服务server,如果服务有效则直接返回该服务
- 构造InterruptTask,它里面有个Timer定时任务,如List-2.2。之后循坏,只要当前线程没有被interrupt,则用subRule的RoundRobin算法选择一个服务实例,如果这个服务有效或者当前时间过了截止时间则跳出循坏
- 如果步骤3中得到的服务实例无效,且当前时间在截止时间之内,则调用Thread.yield(),让出线程资源给其它线程
通过源码可知,RetryRule在subRule.choose获得无效的服务实例后,仅仅是再次再次用subRule获取服务实例,并不会一直尝试下去,即尝试一次。
List-2.2
public class InterruptTask extends TimerTask {
static Timer timer = new Timer("InterruptTimer", true);
protected Thread target = null;
public InterruptTask(long millis) {
this.target = Thread.currentThread();
timer.schedule(this, millis);
}
public boolean cancel() {
try {
return super.cancel();
} catch (Exception var2) {
return false;
}
}
public void run() {
if (this.target != null && this.target.isAlive()) {
this.target.interrupt();
}
}
...