通过之前的文章可以知道, Ribbon负载均衡器选择服务实例的方式是通过“选择策略”实现的, Ribbon实现了很多种选择策略,UML静态类图如上图。 IRule是负载均衡的策略接口,表示某一种规则作为负载均衡的算法,负载平衡策略包括 循环,基于响应时间等。 AbstractLoadBalancerRule 提供获取负载均衡器ILoadBalancer 的默认实现,所有负载策略都继承AbstractLoadBalancerRule。
RoundRobinRule策略实现按照线性轮询的方式依次选择每个服务实例的功能,这也是Ribbon的默认使用策略, 该策略的工作原理
/**
*
* 方便阅读,省略了部分不重要的代码
*/
public class RoundRobinRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter;//原子操作的计数器
public Server choose(ILoadBalancer lb, Object key) {
//。。。略
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
//获取线上和线下的所有服务实例
List allServers = lb.getAllServers();
int serverCount = allServers.size();
//nextServerCyclicCounter计数器+1并通过取模的方式得到序号
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
//。。。略
//如果服务在线上的可用的则使用
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
//。。。略
return server;
}
//其他代码省略
}
WeightedResponseTimeRule策略是对RoundRobinRule的扩展,根据实例运行情况的权重来挑选实例,以达到更优的分配效果,这是一种使用平均/百分比响应时间为每个服务器分配动态“权重”的规则,这种处理规则的原理如下
1. 启动一个30秒执行的定时任务,用来为每个服务实力计算全重,先根据每个实例的平均响应时间得到总平均响应时间,然后为负载均衡器中维护的实例依次计算权重。
假设 A(t=10)、B(t=30)、C(t=40),D(t=20) ,总时间=100
A的权重=100-10=90
B的权重=90+(100-30)=160
C的权重=160+(100-40)=220
D的权重=220+(100-20)=300
2. 分配的时候随即生成1个数字,按照数字和全重 确认发送给哪一个客户端,如下
假设4个端点:A(wt=90)、B(wt=160)、C(wt=220),D(wt=300)。使用Random API,生成一个介于1和最大权重(90+160+220+300)之间的随机数。
基于权重,我们间隔时间如下:
1---90(A的重量) ,如果随机数是1---90则将请求发送给A
91---250(A的重量+B的重量) ,如果随机数是91---250则将请求发送给B
251---470(A的重量+B的重量+C的重量) 如果随机数是251---470 则将请求发送给C
471---770(A的重量+B的重量+C的重量+D的重量) ,如果生成的随机数是471---770,则将请求发送给D
//为方便阅读删除部分代码
public class WeightedResponseTimeRule extends RoundRobinRule {
public Server choose(ILoadBalancer lb, Object key) {
// last one in the list is the sum of all weights
// 最后1个是权重总和
double maxTotalWeight = currentWeights.size() == 0 ? 0 : currentWeights.get(currentWeights.size() - 1);
// 如果权重值小于0.001,则采用父类线性轮询均衡策略
if (maxTotalWeight < 0.001d) {
server = super.choose(getLoadBalancer(), key);
if(server == null) {
return server;
}
} else {
// generate a random weight between 0 (inclusive) to maxTotalWeight (exclusive)
//如果大于等于0.001,则生成1个0到最大权重之前的数字
double randomWeight = random.nextDouble() * maxTotalWeight;
// pick the server index based on the randomIndex
int n = 0;
for (Double d : currentWeights) {
//遍历清单如果权重大于随机数则选择此服务
if (d >= randomWeight) {
serverIndex = n;
break;
} else {
n++;
}
}
server = allList.get(serverIndex);
}
if (server.isAlive()) {
return (server);
}
}
return server;
}
class DynamicServerWeightTask extends TimerTask {
public void run() {
//启动一个30秒执行的定时任务,用来为每个服务实力计算全重
}
}
}
RandomRule策略实现从服务器实例清单中随机选择一个服务实例,策略的实现原理是通过Random对象和服务实例数量,生成一个随机数,通过随机数来确定取那个服务实例,
程序中Thread.yield() 方法,使当前线程由执行状态变成为就绪状态,会把自己CPU执行的时间让掉, 并让自己或者其它的线程运行
// 方便阅读省略部分代码
public class RandomRule extends AbstractLoadBalancerRule {
Random rand;
public Server choose(ILoadBalancer lb, Object key) {
// 。。。
Server server = null;//重置选取server为空
while (server == null) {
//。。。
List allList = lb.getAllServers();
int serverCount = allList.size();
//。。。
根据注册的所有服务数量生成随即数并获取指定的服务
int index = rand.nextInt(serverCount);
server = upList.get(index);
//服务部已不存在,则让出CPU时间
if (server == null) {
Thread.yield();//让出cpu时间
continue;
}
//服务实力在线状态,则使用此服务
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
// 没想到Spring也有这样的注释- -!
server = null;
Thread.yield();
}
return server;
}
//。。。
}
RetryRule策略实现一个具备重试机制的实例选择功能,实现默认使用策略还是RoundRobinRule,只是增加了尝试机制 ,在设置的阈值时间范围内,如果没有获取到服务实例就继续尝试获取服务实例
// 方便阅读省略部分代码
public class RetryRule extends AbstractLoadBalancerRule {
IRule subRule = new RoundRobinRule();
long maxRetryMillis = 500;//设置小于0的时,默认值
public void setMaxRetryMillis(long maxRetryMillis) {
//设置maxRetryMillis 阈值时间
}
public Server choose(ILoadBalancer lb, Object key) {
long requestTime = System.currentTimeMillis();
long deadline = requestTime + maxRetryMillis;
Server answer = null;
//通过线形轮训策略获取服务实例
answer = subRule.choose(key);
//没有得到服务就继续尝试,直到超出设置的阈值
if (((answer == null) || (!answer.isAlive()))
&& (System.currentTimeMillis() < deadline)) {
InterruptTask task = new InterruptTask(deadline
- System.currentTimeMillis());
while (!Thread.interrupted()) {
answer = subRule.choose(key);
if (((answer == null) || (!answer.isAlive()))
&& (System.currentTimeMillis() < deadline)) {
Thread.yield();
} else {
break;
}
}
task.cancel();
}
if ((answer == null) || (!answer.isAlive())) {
return null;
} else {
return answer;
}
}
//。。。。
}
ClientConfigEnableRundRobinRule策略本身并没有实现什么特殊的处理逻辑,内部是调用RoundRobinRule线性轮询机制处理, 此类的目的应该是相让用户通过继承复写此choose方法实现更高级的策略, 从UML图中可以看出BestAvailableRule就是通过复写choose方法实现自身策略
public class ClientConfigEnabledRoundRobinRule extends AbstractLoadBalancerRule {
RoundRobinRule roundRobinRule = new RoundRobinRule();
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
roundRobinRule = new RoundRobinRule();
}
@Override
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
roundRobinRule.setLoadBalancer(lb);
}
@Override
public Server choose(Object key) {
if (roundRobinRule != null) {
return roundRobinRule.choose(key);
} else {
throw new IllegalArgumentException(
"This class has not been initialized with the RoundRobinRule class");
}
}
}
BestAvailableRule 继承ClientConfigEnableRundRobinRule类,依赖负载均衡器的统计对象LoadBalancersStats, BestAvailableRule策略是利用负载均衡器的统计信息选出最空闲的实例
public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule {
private LoadBalancerStats loadBalancerStats;
@Override
public Server choose(Object key) {
//统计信息为空的时候使用线形轮询的策略获取服务实例
if (loadBalancerStats == null) {
return super.choose(key);
}
//统计信息不为空信息的时候遍历所有线上线下服务实例,找出线上服务请求数最小的服务实例
List 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;
}
}
抽象类PredicateBasedRule定义了一个抽象的getPredicate()方法得到一个AbstractServerPredicate对象的实现。在choose方法中,通过AbstractServerPredicate 的实例对象过滤一部分服务然后线性轮训选出服务实例。
AvailabilityFilteringRule策略规则继承了PredicateBasedRule。getPredicate()返回AvailabilityPredicate 对象,策略轮询获取服务,如果满足AvailabilityPredicate 规则就返回服务
@Override
public Server choose(Object key) {
int count = 0;
//先轮询获取服务
Server server = roundRobinRule.choose(key);
//循环10次尝试找到满足条件的服务
while (count++ <= 10) {
if (predicate.apply(new PredicateKey(server))) {
return server;
}
server = roundRobinRule.choose(key);
}
//因为只尝试10次,如果集群很大的情况也可能找不到合适的,
//此时使用父类策略当作备选,即满足条件的服务器按照线性轮训的方式来负载均衡
return super.choose(key);
}
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;
}
ZoneAvoidanceRule 策略规则是一种根据区域和可用性筛选服务器的组合规则
//为方便阅读过滤部分代码
public class ZoneAvoidanceRule extends PredicateBasedRule {
private static final Random random = new Random();
private CompositePredicate compositePredicate;
public ZoneAvoidanceRule() {
super();
//Zone区域筛选条件必须可用
ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this);
//服务可用性筛选条件
AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this);
//compositePredicate 等于两个条件组合
compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
}
@Override
public AbstractServerPredicate getPredicate() {
return compositePredicate;
}
}
上一篇:Spring Cloud Ribbon 源代码学习笔记