上一篇学习到了LoadBalance的其中一个关键组成部分是负载均衡策略,其最基础的接口是IRule。
public interface IRule{
/*
* choose one alive server from lb.allServers or
* lb.upServers according to key
*
* @return choosen Server object. NULL is returned if none
* server is available
*/
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
我们可以看到Ribbon已经实现接口的:
我们挨个看一遍,
首先是AvailabilityFilteringRule,他会根据把特殊情况的server排除,
特殊情况包括 1:in circuit breaker tripped state due to consecutive connection or read failures
2: have active connections that exceeds a configurable limit
一个是处于短路状态,一个是超过了最大连接数。
过滤逻辑在AvailabilityPredicate中,
@Override
public boolean apply(@Nullable PredicateKey input) {
LoadBalancerStats stats = getLBStats();
if (stats == null) {
return true;
}
return !shouldSkipServer(stats.getSingleServerStat(input.getServer()));
}
private boolean shouldSkipServer(ServerStats stats) {
if ((CIRCUIT_BREAKER_FILTERING.get() && stats.isCircuitBreakerTripped())
|| stats.getActiveRequestsCount() >= activeConnectionsLimit.get()) {
return true;
}
return false;
}
public Server choose(Object key) {
if (loadBalancerStats == null) {
return super.choose(key);
}
List<Server> serverList = getLoadBalancer().getAllServers();
int minimalConcurrentConnections = Integer.MAX_VALUE;
long currentTime = System.currentTimeMillis();
Server chosen = null;
for (Server server: serverList) {
ServerStats serverStats = 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;
}
}
我们可以看到,比较简单,遍历一遍所有的server,去除掉短路状态的服务后,选择出并发量最小的。
接下来是RandomRule,随机选择一个服务。
接下来是RetryRule,在已经有的规则上增加重试机制,默认情况下重试机制的规则是RoundRobinRule。
接下来是RoundRobinRule,轮训的方式选择server,其主要是保存了一个 AtomicInteger nextServerCyclicCounter;利用CAS保证线程安全。
其计算index的算法如下:
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
其中modulo就是服务的总数量。
最后一个是WeightedResponseTimeRule,动态的利用response时间计算全职。
doc中的例子非常好:
* The basic idea for weighted round robin has been obtained from JCS
* The implementation for choosing the endpoint from the list of endpoints
* is as follows:Let's assume 4 endpoints:A(wt=10), B(wt=30), C(wt=40),
* D(wt=20).
* <p>
* Using the Random API, generate a random number between 1 and10+30+40+20.
* Let's assume that the above list is randomized. Based on the weights, we
* have intervals as follows:
* <p>
* 1-----10 (A's weight)
* <br>
* 11----40 (A's weight + B's weight)
* <br>
* 41----80 (A's weight + B's weight + C's weight)
* <br>
* 81----100(A's weight + B's weight + C's weight + C's weight)
* <p>
* Here's the psuedo code for deciding where to send the request:
* <p>
* if (random_number between 1 & 10) {send request to A;}
* <br>
* else if (random_number between 11 & 40) {send request to B;}
* <br>
* else if (random_number between 41 & 80) {send request to C;}
* <br>
* else if (random_number between 81 & 100) {send request to D;}
* <p>
* When there is not enough statistics gathered for the servers, this rule
* will fall back to use {@link RoundRobinRule}.
其实就是把总体看做一个圆盘,权重越大所占的面积越大,随机数落在其区域的概率越大。当统计稿率不足时,使用RoundRobinRule。
// holds the accumulated weight from index 0 to current index
// for example, element at index 2 holds the sum of weight of servers from 0 to 2
private volatile List<Double> accumulatedWeights = new ArrayList<Double>();
其中把累加概率保存到了一个列表里,每次计算一个随机数,第一个大于随机数的位置,就是待选泽的server位置。
今天先到这里,接下来继续学习