8.负载均衡器
一、引子
从负载均衡策略可以了解到ribbon实现的各种负载均衡策略,当然也可以自己实现相应的算法。其中主要的接口IRule中有个定义如下:
public ILoadBalancer getLoadBalancer();
算法使用的server列表以及状态信息都从它获取,这里,就从它开始。
二、接口
ILoadBalancer,从源码的注释中很容易看出,它定义负载均衡的操作,并对server进行存储。接口如下:
public interface ILoadBalancer {
public void addServers(List<Server> newServers);
public Server chooseServer(Object key);
public void markServerDown(Server server);
public List<Server> getReachableServers();
public List<Server> getAllServers();
}
那么ILoadBalancer的实现有哪些,参考下图:
三、实现
-
首先看一下addServer如何实现
addServers(List newServers)的默认实现为BaseLoadBalancer,其内部持有两个list,如下:protected volatile List<Server> allServerList; protected volatile List<Server> upServerList;
allServerList代表所有的server,
upServerList代表已经起来的server。
addServers实现其实也比较简单,即将server列表付给这两个成员变量。 -
那么如和区分server是否起来了呢?
其实实现是比较简单的,BaseLoadBalancer内部启动定时任务,每30秒执行一次ping任务,将ping通的server赋给upServerList。
这里涉及到的ping就是我们之前说过的ping。 -
有了上面的实现,那么剩余的方法也就很容易了:
public List<Server> getReachableServers() { return Collections.unmodifiableList(upServerList); } public List<Server> getAllServers() { return Collections.unmodifiableList(allServerList); }
其实现仅仅是返回相应的成员变量。
-
可以看到BaseLoadBalancer有一个子类DynamicServerListLoadBalancer
从它的名字DynamicServerList可以看出它具有动态更新Server类别的功能,其实这个功能实现比较简单,就是依赖ServerListUpdater实现的。
它获取到server列表后,再委托ServerListFilter进行server过滤,最后设置到BaseLoadBalancer的两个成员变量中:allServerList,upServerList。
到这里,我们来张图简单总结一下server列表是如何动态更新的:- ServerListUpdater通过定时任务发起server列表获取。
- ServerList通过EurekaClient获取server列表。
- EurekaClient发起http请求到EurekaServer,获取server列表。
- ServerListUpdater获取结果回调ILoadBalancer设置结果。
- ILoadBalancer通过ServerListFilter进行过滤。
- IPing定时ping,检测server的活性。
这里需要提一点,在状态统计中LoadBalancerStats持有一个map,对应着zone和server列表,它的更新就来自于上面的流程,即DynamicServerListLoadBalancer最后获取到server列表后,会对LoadBalancerStats的map进行更新。
-
ZoneAwareLoadBalancer
此类继承自DynamicServerListLoadBalancer,是负载均衡器的最终实现,也是ribbon默认使用的类。它有如下功能:(翻译自该类的注释):- 一般来说,当选择server时能够避免某个不健康的zone。
- 衡量zone健康状况的关键指标是平均活跃请求,即某个zone的平均活跃请求=这个zone中,还未完成的请求/存活的server
当某个状况不好的zone慢慢的发生超时的时候(即大量的请求被卡住),这个指标是非常有效的。 - 此均衡器将会计算所有的存活的zone的状况:
当任何某个zone平均活跃请求达到配置的阈值时,这个zone将会从被从存活列表中移除;
如果多个zone达到阈值,那么具有平均server请求量最大的zone将会被移除; - 一旦状况最坏的zone被移除,那么将会按照zone具有的实例数量作为概率来选择某个zone。
- 选择完zone,将会使用IRule来选择一个server,默认的实现为ZoneAvoidanceRule(由于此时只有一个zone,故ZoneAvoidanceRule退化为AvailabilityFilteringRule)。
注:ZoneAwareLoadBalancer使用了ZoneAvoidanceRule来实现,对应如下三个方法:
- ZoneAvoidanceRule.createSnapshot
- ZoneAvoidanceRule.getAvailableZones
- ZoneAvoidanceRule.randomChooseZone
- 而这三个方法都是静态方法,ZoneAvoidanceRule也使用了其中的方法来实现zone的排除和筛选,也就是说ZoneAwareLoadBalancer的zone过滤选择和ZoneAvoidanceRule是一样的但是ZoneAwareLoadBalancer采用了ZoneAvoidanceRule的静态方法的方式实现,感觉有些ugly。
另外,ZoneAwareLoadBalancer持有一个map,为每个zone都单独创建了一个BaseLoadBalancer来进行过滤选定一个zone之后的server选择。
-
最后,来看一下核心方法chooseServer是如何实现的,我们已经知道了最终负载均衡的实现是ZoneAwareLoadBalancer,而它也重写了chooseServer,它的choose过程如下:
单个zone的choose过程说明:- 从LoadBalancerStats查询存活的zone数量。
- 发现为1,调用BaseLoadBalancer的chooseServer
- BaseLoadBalancer的实现很简单,直接委托给具体的Rule来选择
- 最终会使用ZoneAvoidanceRule,而此时zone的数量为1,ZoneAvoidanceRule的zone过滤选择功能不起作用,退化为AvailabilityFilteringRule,即按照server断路器打开或者并发量过大过滤掉一些server,再轮询选择一个。
多个zone的choose过程说明:
- 从LoadBalancerStats查询存活的zone数量。
- 发现>1,则调用ZoneAvoidanceRule进行zone的过滤和挑选。
- 挑选好zone之后,跟 单个zone的choose过程一样了,委托给BaseLoadBalancer来进行具体的server挑选。
上一篇: 小米官宣新机:小米9T即将登场