欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

8.负载均衡器

程序员文章站 2022-06-22 22:16:14
...

一、引子

负载均衡策略可以了解到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的实现有哪些,参考下图:
8.负载均衡器

三、实现

  1. 首先看一下addServer如何实现
    addServers(List newServers)的默认实现为BaseLoadBalancer,其内部持有两个list,如下:

    protected volatile List<Server> allServerList;
    protected volatile List<Server> upServerList;
    

    allServerList代表所有的server,
    upServerList代表已经起来的server。
    addServers实现其实也比较简单,即将server列表付给这两个成员变量。

  2. 那么如和区分server是否起来了呢?
    其实实现是比较简单的,BaseLoadBalancer内部启动定时任务,每30秒执行一次ping任务,将ping通的server赋给upServerList。
    这里涉及到的ping就是我们之前说过的ping

  3. 有了上面的实现,那么剩余的方法也就很容易了:

     public List<Server> getReachableServers() {
         return Collections.unmodifiableList(upServerList);
     }
     public List<Server> getAllServers() {
         return Collections.unmodifiableList(allServerList);
     }
    

    其实现仅仅是返回相应的成员变量。

  4. 可以看到BaseLoadBalancer有一个子类DynamicServerListLoadBalancer
    从它的名字DynamicServerList可以看出它具有动态更新Server类别的功能,其实这个功能实现比较简单,就是依赖ServerListUpdater实现的。
    它获取到server列表后,再委托ServerListFilter进行server过滤,最后设置到BaseLoadBalancer的两个成员变量中:allServerList,upServerList。
    到这里,我们来张图简单总结一下server列表是如何动态更新的:
    8.负载均衡器

    1. ServerListUpdater通过定时任务发起server列表获取。
    2. ServerList通过EurekaClient获取server列表。
    3. EurekaClient发起http请求到EurekaServer,获取server列表。
    4. ServerListUpdater获取结果回调ILoadBalancer设置结果。
    5. ILoadBalancer通过ServerListFilter进行过滤。
    6. IPing定时ping,检测server的活性。
      这里需要提一点,在状态统计中LoadBalancerStats持有一个map,对应着zone和server列表,它的更新就来自于上面的流程,即DynamicServerListLoadBalancer最后获取到server列表后,会对LoadBalancerStats的map进行更新。
  5. ZoneAwareLoadBalancer
    此类继承自DynamicServerListLoadBalancer,是负载均衡器的最终实现,也是ribbon默认使用的类。它有如下功能:(翻译自该类的注释):

    1. 一般来说,当选择server时能够避免某个不健康的zone。
    2. 衡量zone健康状况的关键指标是平均活跃请求,即某个zone的平均活跃请求=这个zone中,还未完成的请求/存活的server
      当某个状况不好的zone慢慢的发生超时的时候(即大量的请求被卡住),这个指标是非常有效的。
    3. 此均衡器将会计算所有的存活的zone的状况:
      当任何某个zone平均活跃请求达到配置的阈值时,这个zone将会从被从存活列表中移除;
      如果多个zone达到阈值,那么具有平均server请求量最大的zone将会被移除;
    4. 一旦状况最坏的zone被移除,那么将会按照zone具有的实例数量作为概率来选择某个zone。
    5. 选择完zone,将会使用IRule来选择一个server,默认的实现为ZoneAvoidanceRule(由于此时只有一个zone,故ZoneAvoidanceRule退化为AvailabilityFilteringRule)。

    注:ZoneAwareLoadBalancer使用了ZoneAvoidanceRule来实现,对应如下三个方法:

    1. ZoneAvoidanceRule.createSnapshot
    2. ZoneAvoidanceRule.getAvailableZones
    3. ZoneAvoidanceRule.randomChooseZone
    4. 而这三个方法都是静态方法,ZoneAvoidanceRule也使用了其中的方法来实现zone的排除和筛选,也就是说ZoneAwareLoadBalancer的zone过滤选择和ZoneAvoidanceRule是一样的但是ZoneAwareLoadBalancer采用了ZoneAvoidanceRule的静态方法的方式实现,感觉有些ugly。

    另外,ZoneAwareLoadBalancer持有一个map,为每个zone都单独创建了一个BaseLoadBalancer来进行过滤选定一个zone之后的server选择。

  6. 最后,来看一下核心方法chooseServer是如何实现的,我们已经知道了最终负载均衡的实现是ZoneAwareLoadBalancer,而它也重写了chooseServer,它的choose过程如下:
    8.负载均衡器
    单个zone的choose过程说明:

    1. 从LoadBalancerStats查询存活的zone数量。
    2. 发现为1,调用BaseLoadBalancer的chooseServer
    3. BaseLoadBalancer的实现很简单,直接委托给具体的Rule来选择
    4. 最终会使用ZoneAvoidanceRule,而此时zone的数量为1,ZoneAvoidanceRule的zone过滤选择功能不起作用,退化为AvailabilityFilteringRule,即按照server断路器打开或者并发量过大过滤掉一些server,再轮询选择一个。

    多个zone的choose过程说明:

    1. 从LoadBalancerStats查询存活的zone数量。
    2. 发现>1,则调用ZoneAvoidanceRule进行zone的过滤和挑选。
    3. 挑选好zone之后,跟 单个zone的choose过程一样了,委托给BaseLoadBalancer来进行具体的server挑选。