如果你是房间里最聪明的人,那么你走错房间了。
–> 返回专栏总目录 <–
代码下载地址:https://github.com/f641385712/netflix-learning
目录
前言
本文继续介绍IRule
其它规则实现:BestAvailableRule
最小并发数规则以及PredicateBasedRule
基于断言器实现的的两种方式:AvailabilityFilteringRule
和ZoneAvoidanceRule
。
本文介绍的规则不是简单的轮询,它更关注可用性如:zone的可用性,以及每台Server自己的可用性方面,这些规则适用于大规模集群or多分区、多可用区环境的负载均衡策略。
正文
本文所有规则实现均是ClientConfigEnabledRoundRobinRule
的子类,顾名思义它的规则是可以通过ClientConfig
来配置的,并非是固定的。
ClientConfigEnabledRoundRobinRule
它选择策略的实现很简单,内部定义了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 最小并发数规则
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列表均来自于它~
PredicateBasedRule 基于断言器规则
顾名思义,它是基于断言器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
这两个断言器,因此在了解了断言器的原理的前提下,去看此篇文章将毫无障碍。
AvailabilityFilteringRule 可用性过滤规则
它依赖于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逻辑是:
- 先轮询选一台Server出来,交给
predicate
去判断是否合格(木有被熔断,且活跃连接数没超过阈值才算合格,具体参考AvailabilityPredicate
的逻辑),若合格就直接返回,否则重复此动作一共重复10次- 它能实现故障实例的自动T除,这个特点在大规模集群下特别实用
- 若10此都还没找到合格的,那就调用父类兜底
getPredicate().chooseRoundRobinAfterFiltering()
去轮询一台出来
为何不直接使用父类的逻辑呢?毕竟也会先过滤呀再轮询呀。这其实是子类为性能考虑的一个小技巧,自己先向后试10次,而不用每次都遍历所有的Server再从中选一台,这样对大集群的效率是提高不少的(试想一下你的集群有1000台机器,那这么做会解决不少时间的。因为试10次能够处理99.99%的case,非常划算)。
ZoneAvoidanceRule 可用区规则
它使用的是一个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。- 需要注意的是,这是两个并且的关系
- fallback过滤器为
AvailabilityPredicate
一个(也就是不考虑zone的情况,注意:默认配置下只有主过滤器一个Server都没留下时才会启用fallback去过滤)- 另需注意,fallback会用
AbstractServerPredicate.alwaysTrue()
兜底,以保证至少至少能有一台Server返回,毕竟处理慢总比不处理要好
- 另需注意,fallback会用
备注:还是文首那句话,建议请一定先了解
AbstractServerPredicate
基础过滤组件后再阅读本文,效果更佳
本类并没有重写choose方法,而只需提供此过滤器就好,所以它的choose逻辑是:经过CompositePredicate
组合过滤器过滤后剩下的Server,执行线性轮询找到一台Server。
另外,需要注意的是,本类提供了多个static工具方法:ZoneAvoidanceRule#getAvailableZones、randomChooseZone、createSnapshot
这在前面文章都详细阐述过,可出门左拐参阅。
总结
关于可配置的规则如BestAvailableRule、AvailabilityFilteringRule、ZoneAvoidanceRule
等就先介绍到这了。本文所介绍的规则有如下特点:
- 规则均是可通过
IClientConfig
动态配置的(比如配置负载阈值、熔断比例等等) - 关注zone可用区的可用性、server本身的负载可用性等
- 兜底方案均为线性轮询规则
声明
原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭
。你也可【左边扫码/或加wx:fsx641385712】邀请你加入我的 Java高工、架构师 系列群大家庭学习和交流。
- [享学Netflix] 一、Apache Commons Configuration:你身边的配置管理专家
- [享学Netflix] 二、Apache Commons Configuration事件监听机制及使用ReloadingStrategy实现热更新
- [享学Netflix] 三、Apache Commons Configuration2.x全新的事件-监听机制
- [享学Netflix] 四、Apache Commons Configuration2.x文件定位系统FileLocator和FileHandler
- [享学Netflix] 五、Apache Commons Configuration2.x别样的Builder模式:ConfigurationBuilder
- [享学Netflix] 六、Apache Commons Configuration2.x快速构建工具Parameters和Configurations
- [享学Netflix] 七、Apache Commons Configuration2.x如何实现文件热加载/热更新?
- [享学Netflix] 八、Apache Commons Configuration2.x相较于1.x使用上带来哪些差异?
- [享学Netflix] 九、Archaius配置管理库:初体验及基础API详解
- [享学Netflix] 十、Archaius对Commons Configuration核心API Configuration的扩展实现
- [享学Netflix] 十一、Archaius配置管理器ConfigurationManager和动态属性支持DynamicPropertySupport
- [享学Netflix] 十二、Archaius动态属性DynamicProperty原理详解(重要)
- [享学Netflix] 十三、Archaius属性抽象Property和PropertyWrapper详解
- [享学Netflix] 十四、Archaius如何对多环境、多区域、多云部署提供配置支持?
- [享学Netflix] 十五、Archaius和Spring Cloud的集成:spring-cloud-starter-netflix-archaius
- [享学Netflix] 十六、Hystrix断路器:初体验及RxJava简介
- [享学Netflix] 十七、Hystrix属性抽象以及和Archaius整合实现配置外部化、动态化
- [享学Netflix] 十八、Hystrix配置之:全局配置和实例配置
- [享学Netflix] 十九、Hystrix插件机制:SPI接口介绍和HystrixPlugins详解
- [享学Netflix] 二十、Hystrix跨线程传递数据解决方案:HystrixRequestContext
- [享学Netflix] 二十一、Hystrix指标数据收集(预热):滑动窗口算法(附代码示例)
- [享学Netflix] 二十二、Hystrix事件源与事件流:HystrixEvent和HystrixEventStream
- [享学Netflix] 二十三、Hystrix桶计数器:BucketedCounterStream
- [享学Netflix] 二十四、Hystrix在滑动窗口内统计:BucketedRollingCounterStream、HealthCountsStream
- [享学Netflix] 二十五、Hystrix累计统计流、分发流、最大并发流、配置流、功能流(附代码示例)
- [享学Netflix] 二十六、Hystrix指标数据收集器:HystrixMetrics(HystrixDashboard的数据来源)
- [享学Netflix] 二十七、Hystrix何为断路器的半开状态?HystrixCircuitBreaker详解
- [享学Netflix] 二十八、Hystrix事件计数器EventCounts和执行结果ExecutionResult
- [享学Netflix] 二十九、Hystrix执行过程核心接口:HystrixExecutable、HystrixObservable和HystrixInvokableInfo
- [享学Netflix] 三十、Hystrix的fallback回退/降级逻辑源码解读:getFallbackOrThrowException
- [享学Netflix] 三十一、Hystrix触发fallback降级逻辑的5种情况及代码示例
- [享学Netflix] 三十二、Hystrix抛出HystrixBadRequestException异常为何不会触发熔断?
- [享学Netflix] 三十三、Hystrix执行目标方法时,如何调用线程池资源?
- [享学Netflix] 三十四、Hystrix目标方法执行逻辑源码解读:executeCommandAndObserve
- [享学Netflix] 三十五、Hystrix执行过程集大成者:AbstractCommand详解
- [享学Netflix] 三十六、Hystrix请求命令:HystrixCommand和HystrixObservableCommand
- [享学Netflix] 三十七、源生Ribbon介绍 — 客户端负载均衡器
- [享学Netflix] 三十八、Ribbon核心API源码解析:ribbon-core(一)IClient请求客户端
- [享学Netflix] 三十九、Ribbon核心API源码解析:ribbon-core(二)IClientConfig配置详解
- [享学Netflix] 四十、Ribbon核心API源码解析:ribbon-core(三)RetryHandler重试处理器
- [享学Netflix] 四十一、Ribbon核心API源码解析:ribbon-core(四)ClientException客户端异常
- [享学Netflix] 四十二、Ribbon的LoadBalancer五大组件之:IPing心跳检测
- [享学Netflix] 四十三、Ribbon的LoadBalancer五大组件之:ServerList服务列表
- [享学Netflix] 四十四、netflix-statistics详解,手把手教你写个超简版监控系统
- [享学Netflix] 四十五、Ribbon服务器状态:ServerStats及其断路器原理
- [享学Netflix] 四十六、Ribbon负载均衡策略服务器状态总控:LoadBalancerStats
- [享学Netflix] 四十七、Ribbon多区域选择:ZoneAvoidanceRule.getAvailableZones()获取可用区
- [享学Netflix] 四十八、Ribbon服务器过滤逻辑的基础组件:AbstractServerPredicate
- [享学Netflix] 四十九、Ribbon的LoadBalancer五大组件之:ServerListFilter服务列表过滤器
- [享学Netflix] 五十、Ribbon的LoadBalancer五大组件之:ServerListUpdater服务列表更新器
- [享学Netflix] 五十一、Ribbon的LoadBalancer五大组件之:IRule(一)轮询和加权轮询