Netflix Ribbon 最低并发策略 BestAvailableRule

BestAvailableRule 负载均衡策略会选择当前并发请求量最小的服务实例进行调用。

在运行过程中,BestAvailableRule 会持续监控各个服务实例的并发请求数量。当有新的请求到来时,它会遍历所有可用的服务实例,通过某种机制获取每个实例正在处理的请求数量信息,然后从中挑选出并发请求量最小的那个实例。

最低并发策略特点

  • 动态选择:该策略不是基于固定的规则进行选择,而是根据每个实例的实时并发情况动态地选择最佳实例。

  • 响应速度快:由于选择的是并发量最小的实例,通常意味着该实例的负载较轻,能够更迅速地处理新的请求,从而减少请求的响应时间。

  • 资源合理分配:有助于将请求压力更均匀地分配到各个服务实例上(因为它会选择最闲的服务器进行请求),避免某些实例负载过高而其他实例闲置的情况,实现系统资源的高效利用。

  • 并发感知:该策略能够感知到每个服务实例的并发请求数量,以此作为选择的依据。

配置方式

在 application.properties 中,使用如下配置:

user.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.BestAvailableRule

将在名为 user 的客户端上开启最低并发策略。

源码分析

52d56a8ce21b0f4cbe71fd33bfc5ace7_1729136788642-a467cbbd-e3b8-4452-b3ba-d4c936708120.png

Ribbon 最低并发策略通过 com.netflix.loadbalancer.BestAvailableRule 类实现,代码如下:

public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule {
    // 用于收集和维护负载均衡器的统计信息
    private LoadBalancerStats loadBalancerStats;

    public BestAvailableRule() {
    }

    // 选择最佳服务器
    public Server choose(Object key) {
        if (this.loadBalancerStats == null) {
            // 调用父类,父类采用轮询方式
            return super.choose(key);
        } else {
            // 从负载均衡器中获取所有可用的服务实例列表
            List<Server> serverList = this.getLoadBalancer().getAllServers();
            // 初始化一个整数变量,表示最小的并发连接数
            // 其初始化为整数的最大值,以便在后续的遍历中能够找到更小的值进行更新
            int minimalConcurrentConnections = Integer.MAX_VALUE;
            // 获取当前的时间戳,用于后续判断服务实例的断路器状态
            long currentTime = System.currentTimeMillis();
            // 用于存储最终选择的服务实例
            Server chosen = null;

            // 使用迭代器遍历服务实例列表
            Iterator var7 = serverList.iterator();
            while(var7.hasNext()) {
                Server server = (Server)var7.next();
                // 通过LoadBalancerStats类获取当前服务实例的统计信息。
                // 统计信息可能包括该实例的并发连接数、成功请求数、失败请求数等
                ServerStats serverStats = this.loadBalancerStats.getSingleServerStat(server);
                // 判断当前服务实例的断路器是否跳闸。如果没有跳闸,则继续考虑该实例作为候选;
                // 如果跳闸了,则跳过该实例,继续下一个实例的判断
                if (!serverStats.isCircuitBreakerTripped(currentTime)) {
                    // 获取当前服务实例的并发连接数
                    int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
                    // 判断是否为最小的并发数
                    if (concurrentConnections < minimalConcurrentConnections) {
                        minimalConcurrentConnections = concurrentConnections;
                        chosen = server;
                    }
                }
            }

            // 如果没有选择到服务器,则调用父类 choose 方法选择
            if (chosen == null) {
                return super.choose(key);
            } else {
                return chosen;
            }
        }
    }

    //...
}

最低并发策略父类 ClientConfigEnabledRoundRobinRule 的部分代码如下:

public class ClientConfigEnabledRoundRobinRule extends AbstractLoadBalancerRule {
    // 默认采用轮询策略
    RoundRobinRule roundRobinRule = new RoundRobinRule();

    public ClientConfigEnabledRoundRobinRule() {
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // 默认采用轮询策略
        this.roundRobinRule = new RoundRobinRule();
    }

    public void setLoadBalancer(ILoadBalancer lb) {
        super.setLoadBalancer(lb);
        this.roundRobinRule.setLoadBalancer(lb);
    }

    public Server choose(Object key) {
        if (this.roundRobinRule != null) {
            // 通过轮询策略选择服务器
            return this.roundRobinRule.choose(key);
        } else {
            throw new IllegalArgumentException("This class has not been initialized with the RoundRobinRule class");
        }
    }
}

判断断路器是否跳闸(isCircuitBreakerTripped)的代码见 ServerStats 类:

public boolean isCircuitBreakerTripped(long currentTime) {
    // 跳闸超时时间
    long circuitBreakerTimeout = this.getCircuitBreakerTimeout();
    if (circuitBreakerTimeout <= 0L) {
        return false;
    } else {
        // 如果当前时间还在跳闸超时时间内,则认为跳闸
        return circuitBreakerTimeout > currentTime;
    }
}

// 用于计算断路器的超时时间
private long getCircuitBreakerTimeout() {
    long blackOutPeriod = this.getCircuitBreakerBlackoutPeriod();
    // lastConnectionFailedTimestamp 表示上次连接失败的时间戳
    // blackOutPeriod 表示跳闸的窗口大小,如:30秒
    // 断路器从最后一次失败开始,跳闸30秒
    return blackOutPeriod <= 0L ? 0L : this.lastConnectionFailedTimestamp + blackOutPeriod;
}

通过这种方式,Ribbon 可以在服务出现问题时,根据设定的断路器规则,自动判断是否应该阻止对特定服务实例的请求,从而提高系统的稳定性和可靠性。同时,这种机制也可以让系统在服务恢复正常后,自动解除断路器的限制,继续向该服务实例发送请求。

最低并发策略优缺点

优点

  • 提高响应速度:选择并发请求量最小的实例通常意味着该实例的负载较轻,能够更快地处理请求,从而提高整体的响应速度。

  • 优化资源利用:可以有效地将请求分配到负载较轻的服务实例上,避免某些实例负载过高而其他实例闲置的情况,优化了系统资源的利用。

缺点

  • 统计准确性问题:准确统计每个服务实例的并发请求数量可能存在一定的难度,特别是在高并发的情况下,可能会出现统计不准确的情况。

  • 额外开销:为了统计并发请求数量,需要额外的资源和计算开销。

适用场景

  • 对响应时间要求较高的系统:例如实时交易系统、在线游戏等,需要快速响应客户请求,BestAvailableRule 策略可以帮助选择负载较轻的服务实例,提高响应速度。

  • 资源有限的环境:当系统的资源有限,不能承受过高的负载时,可以使用该策略将请求分配到负载较轻的实例上,以确保系统的稳定性。

  • 服务实例性能差异较大的场景:如果服务实例的性能差异较大,BestAvailableRule 可以选择性能较好的实例进行调用,提高系统的整体性能。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号