任何你写的代码,超过6个月不去看它,当你再看时,都像是别人写的。
–> 返回专栏总目录 <–
代码下载地址:https://github.com/f641385712/netflix-learning
前言
关于IRule的实现,还差两个实现规则,一个是随机规则RandomRule
,一个是重试规则RetryRule
,本文将进行收尾,并且给出对所有IRule实现的总结列表。
正文
RandomRule 随机规则
随机选择一个server。使用ThreadLocalRandom.current().nextInt(serverCount);
随机来一个。
public class RandomRule extends AbstractLoadBalancerRule {
public Server choose(ILoadBalancer lb, Object key) {
...
Server server = null;
while (server == null) {
...
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
int index = chooseRandomInt(serverCount);
server = upList.get(index);
...
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
}
说明:它的choose方法代码同
RoundRobinRule
的高度相似,但是RoundRobinRule
计算随机值和get的时候使用的均是allServers
,所以是没有bug的。但是本文是有bug的下,下面会使用示例模拟
这个算法非常的简单,但是,但是,但是,有个小细节你需要特别注意:
- 随机数是通过
allList.size()
,也就是all所有Server的size - 但是,取值的时候却用
upList.get(index)
来取值
这很明显是个bug,下面我们通过代码示例来验证这个bug。
代码演示RandomRule的bug
@Test
public void fun2() throws InterruptedException {
List<Server> serverList = new ArrayList<>();
serverList.add(createServer("华南", 1));
serverList.add(createServer("华东", 1));
serverList.add(createServer("华东", 2));
serverList.add(createServer("华北", 1));
serverList.add(createServer("华北", 2));
serverList.add(createServer("华北", 3));
serverList.add(createServer("华北", 4));
// 轮询策略:因为Servers均来自于lb,所以必须关联上一个lb实例哦
ILoadBalancer lb = new BaseLoadBalancer();
lb.addServers(serverList);
RandomRule rule = new RandomRule();
rule.setLoadBalancer(lb);
System.out.println("server总数:"+lb.getAllServers().size());
System.out.println("up的总数:"+lb.getReachableServers().size());
while (true) {
System.out.println(rule.choose(null));
TimeUnit.SECONDS.sleep(2);
}
}
private Server createServer(String zone, int index) {
Server server = new Server("www.baidu" + zone + ".com", index);
server.setAlive(true);
server.setReadyToServe(true);
server.setZone(zone);
return server;
}
运行程序,控制台输出:
server总数:7
up的总数:7
www.baidu华东.com:1
www.baidu华北.com:1
www.baidu华北.com:1
www.baidu华北.com:2
www.baidu华北.com:1
www.baidu华东.com:1
www.baidu华南.com:1
...
完美,结果是完全随机的。这样不会出任何问题,因为本利all = up
(都是7台)。下面模拟all > up的情况:
因为Server是否up是由IPing去决定的,因此只需要提供自定义的IPing规则便可模拟出现这个bug
lb.setPing(server -> server.getPort() % 10 > 2);
再次运行程序,抛错:
server总数:7
up的总数:2
java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
at java.util.Collections$UnmodifiableList.get(Collections.java:1309)
at com.netflix.loadbalancer.RandomRule.choose(RandomRule.java:61)
at com.netflix.loadbalancer.RandomRule.choose(RandomRule.java:92)
因为upList
只有2台,而总数是7台,所以如果随机出来的数字加入是3,那get(index)
就抛错啦~
使用场景
几乎不用。更何况它还有bug呢,更不用喽。由于Ribbon工程目前处于维护状态,且该规则几乎不会使用,因此bug官方也不会修这个bug。万一你偏要使用,请你自行实现而不要使用这个有bug的版本。
RetryRule 重试规则
它可以在给定的任何IRule
的基础上再包一层重试逻辑(默认给定的RoundRobinRule
规则)。
public class RetryRule extends AbstractLoadBalancerRule {
IRule subRule = new RoundRobinRule();
// 最大重试时长 默认值是半分钟
long maxRetryMillis = 500;
... // 省略给这两个属性赋值的构造器和set方法们
@Override
public void setLoadBalancer(ILoadBalancer lb) {
super.setLoadBalancer(lb);
subRule.setLoadBalancer(lb);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
// 选择方法
public Server choose(ILoadBalancer lb, Object key) {
// 计算出一个开始选择 和 结束选择的时间
long requestTime = System.currentTimeMillis();
long deadline = requestTime + maxRetryMillis;
// 先通过subRule选择出一个server
Server answer = null;
answer = subRule.choose(key);
// 如果选择出的Server为null,或者不是活的
// 并且还在结束时间之前,就执行重试策略
if (((answer == null) || (!answer.isAlive()))
&& (System.currentTimeMillis() < deadline)) {
InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis());
while (!Thread.interrupted()) {
... // 持续不断的调用subRule.choose(key),知道获取到或者时间到了
}
task.cancel();
}
// 如果最终还是没获取到可用的,那就返回null。否则返回正常结果
if ((answer == null) || (!answer.isAlive())) {
return null;
} else {
return answer;
}
}
}
RetryRule中又定义了一个subRule,它默认的实现类是RoundRobinRule
(你可以自己指定任何IRule实现),每次先采用subRule#choose()
规则来选择一个服务实例,如果选到的实例正常就返回不需要重试;如果选择的服务实例为null或者已经失效,则在失效时间deadline之前不断的进行重试(重试时获取服务的策略还是subRule#choose()
来选择),如果超过了deadline还是没取到则会返回一个null。
使用场景
因为它属于包装器模式的一种实现,因此在实际生产中,我个人推荐使用它来包装你实际的IRule,这样会使得实例Server更加的健康,对网络波动的容忍度更高些,如你可以这么做new RetryRule(new BestAvailableRule())
。
RetryRule
配合上RoundRobinRule
的组合(也就是默认组合)效果很好:因为RoundRobinRule失效的策略是超过10次,而如果在配合上RetryRule
的话,容错性就会更强(当然相应的rt就会更长喽)~
IRule所有内置规则归纳
最后用一张表格来归纳所有的IRule实现:
规则名 | 父类 | xxxxxxxxxxxxxxxxxxxxx | 备注 |
---|---|---|---|
RoundRobinRule |
- | 线性轮询 | 轮询index,选择index对应位置的server |
WeightedResponseTimeRule |
RoundRobinRule | 根据rt分配一个权重值,rt时间越长,weight越小,被选中的可能性就越低 | 使用一个后台线程默认每30s重新计算一次权重值 |
BestAvailableRule |
ClientConfigEnabled… | 选择一个活跃请求数最小的Server | 忽略已经被熔断的Server |
PredicateBasedRule |
ClientConfigEnabled… | 基于断言器实现的规则 | 本类为抽象类,具体过滤规则交给子类 |
AvailabilityFilteringRule |
PredicateBasedRule | 过滤掉已熔断or活跃请求数太高的Server后,剩下的执行线性轮询 | 依赖于AvailabilityPredicate 这个断言器实现过滤 |
ZoneAvoidanceRule |
PredicateBasedRule | 复合判断。先选出可用区,然后在按上规则筛选出复合条件的Server们,执行线性轮询 | 使用ZoneAvoidancePredicate 和AvailabilityPredicate 两个主断言器实现过滤 |
RandomRule |
- | 完全随机选择 | 此实现有bug,有bug,有bug |
RetryRule |
- | 对任何IRule包一层重试机制 | 在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server |
说明:若是直接extends AbstractLoadBalancerRule
此处不写出,因为所有的实现均继承了它,没必要显示写出
总结
关于IRule的随机规则RandomRule
和重试规则RetryRule
就先介绍到这,本文首先了解了RandomRule
随机规则他在生产环境几乎不会使用,并且重点是它还存在bug,不能使用。而重试规则RetryRule
我个人推荐可以尝试在生产上使用,它使用起来也方便,能够提升系统的健康程度,当然牺牲的可能是rt时间~
到此LoadBalancer
的五大核心组件就全部,非常详细的介绍完了,下章将进入负载均衡的调度中心ILoadBalancer
的深入学习。
声明
原创不易,码字不易,多谢你的点赞、收藏、关注。把本文分享到你的朋友圈是被允许的,但拒绝抄袭
。你也可【左边扫码/或加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(一)轮询和加权轮询
- [享学Netflix] 五十二、Ribbon的LoadBalancer五大组件之:IRule(二)应用于大规模集群的可配置规则