如果你是房间里最聪明的人,那么你走错房间了。
代码下载地址:https://github.com/f641385712/netflix-learning
本文继续介绍IRule
其它规则实现:BestAvailableRule
最小并发数规则以及PredicateBasedRule
基于断言器实现的的两种方式:AvailabilityFilteringRule
和ZoneAvoidanceRule
。
本文介绍的规则不是简单的轮询,它更关注可用性如:zone的可用性,以及每台Server自己的可用性方面,这些规则适用于大规模集群or多分区、多可用区环境的负载均衡策略。
本文所有规则实现均是ClientConfigEnabledRoundRobinRule
的子类,顾名思义它的规则是可以通过ClientConfig
来配置的,并非是固定的。
它选择策略的实现很简单,内部定义了RoundRobinRule
,choose方法还是采用了RoundRobinRule
的choose方法,所以它的选择策略和RoundRobinRule
的选择策略一致。
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);
}
// 若子类不复写 效果同`RoundRobinRule`
@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");
}
}
}
该策略较为特殊,我们一般不直接使用它。因为它本身并没有实现什么特殊的处理逻辑,效果同RoundRobinRule
。通过继承该策略,默认的choose就实现了线性轮询机制,在子类中做一些高级策略时通常可能存在一些无法实施的情况,就可以用父类的实现作为备选,所以它作为父类用于兜底。
设计上,其实把该类Abstract化或许更为合理
它作为父类,有多个子类实现,从而使用各自的算法逻辑:
BestAvailableRule
:PredicateBasedRule
:基于AbstractServerPredicate
实现的规则,有两个子类 ZoneAvoidanceRule
:AvailabilityFilteringRule
:BestAvailableRule继承自ClientConfigEnabledRoundRobinRule
。该策略的特性跳过已经被熔断的实例,并且顺表找出最空闲的实例。BestAvailable
:最空闲、最可用的(也就是并发请求数最低的)。很明显,统计数据来自云LoadBalancerStats/ServerStats
。
public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule {
// 也就是说:LoadBalancerStats是来自于lb的
private LoadBalancerStats loadBalancerStats;
@Override
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
if (lb instanceof AbstractLoadBalancer) {
loadBalancerStats = ((AbstractLoadBalancer) lb).getLoadBalancerStats();
}
}
@Override
public Server choose(Object key) {
// 若没有统计信息,那就回退到轮询策略呗~~~~
if (loadBalancerStats == null) {
return super.choose(key);
}
List<Server> serverList = getLoadBalancer().getAllServers();
// 记录所有Server中最小并发数
int minimalConcurrentConnections = Integer.MAX_VALUE;
long currentTime = System.currentTimeMillis();
Server chosen = null;
for (Server server: serverList) {
ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);
// 只要该Server没有被熔断,就选择上
if (!serverStats.isCircuitBreakerTripped(currentTime)) {
int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
if (concurrentConnections < minimalConcurrentConnections) {
minimalConcurrentConnections = concurrentConnections;
chosen = server;
}
}
// 为毛不判断Server的alive性和是否可服务性呢?
// 我觉得这是个小bug,毕竟你对比的是getAllServers所有服务
// if (server.isAlive() && (server.isReadyToServe()))
}
if (chosen == null) {
return super.choose(key);
} else {
return chosen;
}
}
}
请注意我在源码处标注的小bug,在生产中也是需要引起注意的。
如果loadBalancerStats为null,则BestAvailableRule
将回退到采用它的父类即ClientConfigEnabledRoundRobinRule
的服务选取策略,即线性轮询。
说明:没有
loadBalancerStats
不代表没有ILoadBalancer
。ILoadBalancer是必须的,因为Server列表均来自于它~
顾名思义,它是基于断言器AbstractServerPredicate
来实现Server的筛选。
阅读它之前,请你务必已经了解了
AbstractServerPredicate
的原理,因为它才是核心。关于断言器AbstractServerPredicate
的详解,请参见:[享学Netflix] 四十八、Ribbon服务器过滤逻辑的基础组件:AbstractServerPredicate
// 本类为抽象类,很好
public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
// 抽象方法:具体的断言器交给子类去指定......
public abstract AbstractServerPredicate getPredicate();
@Override
public Server choose(Object key) {
ILoadBalancer lb = getLoadBalancer();
Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
if (server.isPresent()) {
return server.get();
} else {
return null;
}
}
}
它自己是个抽象类,但是它规定了一个整体的负载均衡规则是:
AbstractServerPredicate
断言器过滤然后剩余下一个ServerList它有两个实现子类,分别依赖于AvailabilityPredicate
和ZoneAvoidancePredicate
这两个断言器,因此在了解了断言器的原理的前提下,去看此篇文章将毫无障碍。
它依赖于AvailabilityPredicate
完成过滤后,在使用线性轮询方式选择Server。
public class AvailabilityFilteringRule extends PredicateBasedRule {
private AbstractServerPredicate predicate;
public AvailabilityFilteringRule() {
super();
predicate = CompositePredicate.withPredicate(new AvailabilityPredicate(this, null))
.addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
.build();
}
// 实现父类抽象方法
@Override
public AbstractServerPredicate getPredicate() {
return predicate;
}
...
@Override
public Server choose(Object key) {
int count = 0;
Server server = roundRobinRule.choose(key);
while (count++ <= 10) {
if (predicate.apply(new PredicateKey(server))) {
return server;
}
server = roundRobinRule.choose(key);
}
return super.choose(key);
}
}
它的choose逻辑是:
predicate
去判断是否合格(木有被熔断,且活跃连接数没超过阈值才算合格,具体参考AvailabilityPredicate
的逻辑),若合格就直接返回,否则重复此动作一共重复10次 getPredicate().chooseRoundRobinAfterFiltering()
去轮询一台出来为何不直接使用父类的逻辑呢?毕竟也会先过滤呀再轮询呀。这其实是子类为性能考虑的一个小技巧,自己先向后试10次,而不用每次都遍历所有的Server再从中选一台,这样对大集群的效率是提高不少的(试想一下你的集群有1000台机器,那这么做会解决不少时间的。因为试10次能够处理99.99%的case,非常划算)。
它使用的是一个CompositePredicate
的组合过滤器:
ZoneAvoidanceRule:
private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {
return CompositePredicate.withPredicates(p1, p2)
.addFallbackPredicate(p2)
.addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
.build();
}
解释此过滤器逻辑:
ZoneAvoidancePredicate
和AvailabilityPredicate
两个,也就是说主条件是会过滤掉:熔断率偏高or平均负载过高的zone区域(zone级别的过滤),并且过滤掉已经熔断or活跃请求数过高的Server。 AvailabilityPredicate
一个(也就是不考虑zone的情况,注意:默认配置下只有主过滤器一个Server都没留下时才会启用fallback去过滤) AbstractServerPredicate.alwaysTrue()
兜底,以保证至少至少能有一台Server返回,毕竟处理慢总比不处理要好备注:还是文首那句话,建议请一定先了解
AbstractServerPredicate
基础过滤组件后再阅读本文,效果更佳
本类并没有重写choose方法,而只需提供此过滤器就好,所以它的choose逻辑是:经过CompositePredicate
组合过滤器过滤后剩下的Server,执行线性轮询找到一台Server。
另外,需要注意的是,本类提供了多个static工具方法:ZoneAvoidanceRule#getAvailableZones、randomChooseZone、createSnapshot
这在前面文章都详细阐述过,可出门左拐参阅。
关于可配置的规则如BestAvailableRule、AvailabilityFilteringRule、ZoneAvoidanceRule
等就先介绍到这了。本文所介绍的规则有如下特点:
IClientConfig
动态配置的(比如配置负载阈值、熔断比例等等)