SpringCloud之负载均衡策略
Spring Cloud整合了ribbon作为客户端的负载均衡器,其中提供了⼀些负载均衡的算法,本⽂就了解这些负载均衡的实现:
⼀、负载均衡策略
负载均衡策略的顶层接⼝为IRule,看下其类结构:
public interface IRule{
public Server choo(Object key);
public void tLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
choo(Object key):根据key值从负载均衡器的所有服务列表或存活的服务列表中选择⼀个存活的服务,这⾥的key值⼀般为服务的实例名称。
tLoadBalancer(ILoadBalancer lb):设置⼀个负载均衡器
getLoadBalancer():获取⼀个负载均衡器
接下来,我们看下IRule有哪些实现类,每种实现类都表⽰哪些算法,查看其类图:
AbstractLoadBalancerRule定义了ILoadBalancer实例,通过tLoadBalancer和getLoadBalancer对负载均衡器进⾏存取;RoundRobinRule
最著名和最基本的负载平衡策略,即循环规则。
查看核⼼算法实现的源码:
司马迁报任安书
public Server choo(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server rver = null;
int count = 0;
//若服务为空,或循环次数<10,这⾥为10是不怕总数过⼤⼀直循环影响性能,循环10次获取的实例都为空则直接结束
while (rver == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int rverCount = allServers.size();
//若存活的实例数量或总实例数量=0则返回空
if ((upCount == 0) || (rverCount == 0)) {
log.warn("No up rvers available from load balancer: " + lb);
return null;
}
//获取实例的下标值
int nextServerIndex = incrementAndGetModulo(rverCount);
rver = (nextServerIndex);
//若是获取的服务为空,则继续循环
德育为先
if (rver == null) {
银色的英文/* Transient. */
Thread.yield();
continue;
}
含有山的四字词语//若实例是⽣效的
if (rver.isAlive() && (rver.isReadyToServe())) {
return (rver);
}
// Next.
rver = null;
}
if (count >= 10) {
log.warn("No available alive rvers after 10 tries from load balancer: "
+ lb);
}
return rver;
}
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = ();
//当前下标值+1 与总实例数量取模,会获得⼩于实例数量的值
int next = (current + 1) % modulo;
//当前实例的下标值赋值为得到的下标值
if (pareAndSet(current, next))
return next;
}
}
1.从负载均衡器中获取全部的实例列表和可达的实例列表
2.本地变量维护⼀个原⼦类的nextServerCysclicCounter值,该值+1之后与全部实例列表进⾏取模得到的列表下标对应的服务,另外nextServerCysclicCounter值通过CAS赋值为该下标
RetryRule
考虑到级联,这个类允许向现有规则添加重试逻辑。
public RetryRule() {
}
public RetryRule(IRule subRule) {
this.subRule = (subRule != null) ? subRule : new RoundRobinRule();
}
public RetryRule(IRule subRule, long maxRetryMillis) {
this.subRule = (subRule != null) ? subRule : new RoundRobinRule();
this.maxRetryMillis = (maxRetryMillis > 0) ? maxRetryMillis : 500;
}
该构造函数中可以提供⼀个规则(默认为RoundRobinRule)和最⼤重试的毫秒数(默认为500),⽽retryRule为其提供重试的策略
public Server choo(ILoadBalancer lb, Object key) {
long requestTime = System.currentTimeMillis();
企业管理概论
long deadline = requestTime + maxRetryMillis;
Server answer = null;
answer = subRule.choo(key);
if (((answer == null) || (!answer.isAlive()))
&& (System.currentTimeMillis() < deadline)) {
InterruptTask task = new InterruptTask(deadline
- System.currentTimeMillis());
while (!Thread.interrupted()) {
answer = subRule.choo(key);
if (((answer == null) || (!answer.isAlive()))
&& (System.currentTimeMillis() < deadline)) {
/
* pau and retry hoping it's transient */
Thread.yield();
} el {
break;
}
}
task.cancel();
}
if ((answer == null) || (!answer.isAlive())) {
return null;
} el {
return answer;
}
}
很明显,如果通过设置的算法获取的服务实例为null或者不是存活的话,本算法会提供⼀个重试的机制,重试的时间为设置的最⼤重试时间内,是通过Timer进⾏重试的。
RandomRule
⼀种负荷平衡策略,在现有流量之间随机分配流量。
public Server choo(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server rver = null;
while (rver == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int rverCount = allList.size();
if (rverCount == 0) {
/*
* No rvers. End regardless of pass, becau subquent pass
* only get more restrictive.
李卓远
*/
return null;
}
int index = chooRandomInt(rverCount);
rver = (index);
if (rver == null) {
/*
* The only time this should happen is if the rver list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (rver.isAlive()) {
槟榔芋
return (rver);
}
// Shouldn't actually happen.. but must be transient or a bug.
rver = null;
Thread.yield();
}
return rver;
}
protected int chooRandomInt(int rverCount) {
return ThreadLocalRandom.current().nextInt(rverCount);
}
这种算法⽐较简单,就是从全部的服务列表中进⾏随机抽取⼀个服务实例返回。
ClientConfigEnabledRoundRobinRule
该算法其实就是包含了RoundRoBinRule。该策略⼀般不直接使⽤,有些⾼级的策略会继承该类,完成⼀些⾼级的策略。BestAvailableRule
⼀个跳过断路器,选择具有最低并发请求的服务器的规则。
本规则保存了负载均衡器中的每个节点的操作特性和统计信息。
public Server choo(Object key) {
//当loadBalancerStats为空,采⽤⽗类的线性轮询
//体现了在ClientConfigEnabledRoundRobinRule的⼦类⽆法满⾜⾼级策略时,采⽤
//ClientConfigEnabledRoundRobinRule的线性轮询特性
if (loadBalancerStats == null) {
return super.choo(key);
}
List<Server> rverList = getLoadBalancer().getAllServers();
int minimalConcurrentConnections = Integer.MAX_VALUE;
long currentTime = System.currentTimeMillis();
Server chon = null;
for (Server rver: rverList) {
ServerStats rverStats = SingleServerStat(rver);
/
/过滤掉负载的实例
if (!rverStats.isCircuitBreakerTripped(currentTime)) {
int concurrentConnections = ActiveRequestsCount(currentTime);
//找出请求数最⼩的⼀个
if (concurrentConnections < minimalConcurrentConnections) {
minimalConcurrentConnections = concurrentConnections;
等歌词
chon = rver;
}
}
}
if (chon == null) {
return super.choo(key);
} el {
return chon;
}
}
这个规则对可见的服务器设置了⼀个限制,过滤了负载的实例,这确保它只需要在部分服务器中查找最⼩的并发请求,这避免了⼤量客户端选择⼀个具有最低并发请求的服务器⽽⽴即不堪重负的问题。
PredicateBadRule
PredicateBadRule策略继承ClientConfigEnableRoundRobinRule,该策略主要特性是“先过滤,在轮询”,也就是先过滤掉⼀些实例,得到过滤后的实例清单,然后轮询该实例清单,PredicateBadRule中“过滤”功能没有实现,需要继承它的类完成,也就是说不同继承PredicateBadRule的类有不同的“过滤特性”
AvailabilityFilteringRule
继承⾃PredicateBadRule的⼀个负载平衡规则,过滤掉以下服务器:
连续连接或读取失败,处于断路器"tripped"状态,或有超过可配置限制的活动连接(默认是Integer.MAX_VALUE)