接着上一篇,继续说说 Ribbon 的LoadBalancer
ZoneAwareLoadBalancer
这个类继承了前文中的DynamicServerListLoadBalancer,避免轮询时候跨zone调用实体产生高延迟。主要通过重写了两个方法:
setServerListForZones
1 | private ConcurrentHashMap<String, BaseLoadBalancer> balancers = new ConcurrentHashMap<String, BaseLoadBalancer>(); |
第一个方法创建了一个map,为每一个zone配置一个LoadBalancer,初始情况zone没有对应的LoadBalancer,则调用getLoadBalancer来为zone设置一个。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
BaseLoadBalancer getLoadBalancer(String zone) {
zone = zone.toLowerCase();
BaseLoadBalancer loadBalancer = balancers.get(zone);
if (loadBalancer == null) {
// We need to create rule object for load balancer for each zone
IRule rule = cloneRule(this.getRule());
loadBalancer = new BaseLoadBalancer(this.getName() + "_" + zone, rule, this.getLoadBalancerStats());
BaseLoadBalancer prev = balancers.putIfAbsent(zone, loadBalancer);
if (prev != null) {
loadBalancer = prev;
}
}
return loadBalancer;
}
这个方法简单明了,有Rule就根据config和Rule的class实例化一个,没有就new AvailabilityFilteringRule()。然后new一个BaseLoadBalancer,最后把新的LoadBalancer放到map里。随后在chooseServer的时候发挥作用。在RibbonClientConfiguration中可以得知,最终的默认实现是ZoneAvoidanceRule。
chooseServer
1 |
|
可以看到chooseServer的时候先判断是否要根据区域来进行选择,如果配置了否或者zone只有一个就直接用父类轮询的策略选择。
然后根据LoadBalancerStats创建zoneSnapshot,保存了每个zone的统计信息,包括断路器触发数量,平均负载等信息。然后读取了两个参数,一个是故障比例0.99,一个是服务负载下限0.2。调用ZoneAvoidanceRule.getAvailableZones方法。
委托父类
1 | public static Set<String> getAvailableZones( |
getAvailableZones方法内部创建了两个set,一个用于保存候选zone,一个用来保存最差的zone,最差指的是负载最高的。候选zone的实例数量必须大于0,而且(断路器处罚数量/实例数)不能超过0.99。
如果最高负载比传入的最低负载还要低,所有实例都符合候选条件,就直接返回,大家都可用。不然就在最差区域中随机选一个zone移除掉,返回剩下的候选zone。
getAvailableZones返回后,ZoneAwareLoadBalancer继续判断,如果剔除过zone,并且可用zone不为空,随机选择一个zone,委托这个zone对应的LoadBalancer来进行下一步的选择,很明显这里最终会委托给IRule来进行选择,这个具体的Rule,应该是父类的RoundRobinRule,或者这里新设置的AvailabilityFilteringRule。书上说的是ZoneAvoidanceRule来实现。可能是我有关键信息没找到,不知道怎么会是这个Rule。
这样最终就能实现根据zone来选择服务实例的效果。