【微服务七】Ribbon负载均衡策略之BestAvailableRule源码深度剖析

本文涉及的产品
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 【微服务七】Ribbon负载均衡策略之BestAvailableRule源码深度剖析

@[TOC]

一、前言

前置Ribbon相关文章:

  1. 【云原生&微服务一】SpringCloud之Ribbon实现负载均衡详细案例(集成Eureka、Ribbon)
  2. 【云原生&微服务二】SpringCloud之Ribbon自定义负载均衡策略(含Ribbon核心API)
  3. 【云原生&微服务三】SpringCloud之Ribbon是这样实现负载均衡的(源码剖析@LoadBalanced原理)
  4. 【云原生&微服务四】SpringCloud之Ribbon和Erueka集成的细节全在这了(源码剖析)
  5. 【微服务五】Ribbon随机负载均衡算法如何实现的
  6. 【微服务六】Ribbon负载均衡策略之轮询(RoundRobinRule)、重试(RetryRule)

我们聊了以下问题:

  1. 为什么给RestTemplate类上加上了@LoadBalanced注解就可以使用Ribbon的负载均衡?
  2. SpringCloud是如何集成Ribbon的?
  3. Ribbon如何作用到RestTemplate上的?
  4. 如何获取到Ribbon的ILoadBalancer?
  5. ZoneAwareLoadBalancer(属于ribbon)如何与eureka整合,通过eureka client获取到对应注册表?
  6. ZoneAwareLoadBalancer如何持续从Eureka中获取最新的注册表信息?
  7. 如何根据负载均衡器ILoadBalancer从Eureka Client获取到的List<Server>中选出一个Server?
  8. Ribbon如何发送网络HTTP请求?
  9. Ribbon如何用IPing机制动态检查服务实例是否存活?
  10. Ribbon负载均衡策略之随机(RandomRule)、轮询(RoundRobinRule)、重试(RetryRule)实现方式;

本文继续讨论 最佳可用规则(BestAvailableRule)是如何实现的?

PS:Ribbon依赖Spring Cloud版本信息如下:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.3.7.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--整合spring cloud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR8</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--整合spring cloud alibaba-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.5.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

二、BestAvailableRule

BestAvailableRule会逐个考察Server,如果Server被tripped了,则跳过;最终选择一个并发请求量最小的Server。

1、负载规则

我们知道Ribbon负载均衡算法体现在IRule的choose(Object key)方法中,所以看BestAvailableRule的choose(Object key)方法:

在这里插入图片描述

详细代码注释如下:

public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule {

    // 维护了服务实例的一些状态信息
    private LoadBalancerStats loadBalancerStats;
    
    @Override
    public Server choose(Object key) {
       // 如果服务实例状态信息为空,则直接使用父类的choose()方法,采用RoundRobin算法
        if (loadBalancerStats == null) {
            return super.choose(key);
        }
        // 获取所有的服务实例
        List<Server> serverList = getLoadBalancer().getAllServers();
        // 最小并发连接数
        int minimalConcurrentConnections = Integer.MAX_VALUE;
        // 当前时间
        long currentTime = System.currentTimeMillis();
        Server chosen = null;
        // 遍历每个实例
        for (Server server: serverList) {
            ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);
            // 如果服务实例被tripped了,则直接跳过当前服务实例
            if (!serverStats.isCircuitBreakerTripped(currentTime)) {
                // 获取实例的并发数(当且仅当 当前时间与上次有效更改连接数的时间间隔在指定范围之内(默认10分钟))
                int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
                // 找到并发连接最小的那个服务实例
                if (concurrentConnections < minimalConcurrentConnections) {
                    minimalConcurrentConnections = concurrentConnections;
                    chosen = server;
                }
            }
        }
        // 如果遍历完所有的服务实例之后,还没有找到server,则调用父类的choose()方法,用RoundRobin算法进行选择。
        if (chosen == null) {
            return super.choose(key);
        } else {
            return chosen;
        }
    }

    @Override
    public void setLoadBalancer(ILoadBalancer lb) {
        super.setLoadBalancer(lb);
        if (lb instanceof AbstractLoadBalancer) {
            loadBalancerStats = ((AbstractLoadBalancer) lb).getLoadBalancerStats();            
        }
    }
}

方法的核心逻辑:

  1. 首先判断如果服务实例状态信息为空,则直接使用父类的choose()方法,采用RoundRobin算法。
  2. 否则:从BestAvailableRule所属的ILoadBalancer中获取服务的所有实例,记录当前时间;
  3. 遍历服务的每个实例,获取实例的ServerStats,如果实例被tripped了,则直接跳过当前服务实例;否则,获取实例的并发数(这里当且仅当 当前时间与上次有效更改连接数的时间间隔在指定范围之内(默认10分钟)),如果超过了时间范围则返回0。循环结束后返回并发数最小的第一个实例。
  4. 最后,如果遍历完所有的服务实例之后,还没有得到Server,则调用其父类的choose()方法,使用RoundRobin算法选择一个实例。

下面我们接着看几个细节点:如何判断服务实例被tripped?如何获取服务实例的并发数?

2、如何判断服务实例被tripped?

在这里插入图片描述
逻辑体现在ServerStatsisCircuitBreakerTripped(long currentTime)方法中:

public boolean isCircuitBreakerTripped(long currentTime) {
    // 获取断路器超时时间点
    long circuitBreakerTimeout = getCircuitBreakerTimeout();
    // 如果断路器超时时间点 <= 0,则直接返回false。
    if (circuitBreakerTimeout <= 0) {
        return false;
    }
    // 如果断路器超时时间点 > 当前时间,则返回true,表示服务实例被tripped了;否则返回false
    return circuitBreakerTimeout > currentTime;
}

方法核心逻辑:

  1. 判断断路器超时时间点是否大于当前时间,如果大于,则表示当前服务实例被tripped了,也就不会再被选择;否者,正常选择。

3、如何获取服务实例的并发数?

在这里插入图片描述
逻辑体现在ServerStatsgetActiveRequestsCount(long currentTime)方法中:

public int getActiveRequestsCount(long currentTime) {
  // 获取实例当前的并发连接数
    int count = activeRequestsCount.get();
    // 连接数为0,则直接返回0
    if (count == 0) {
        return 0;
    // 如果当前时间与上次有效更改连接数的时间间隔不在指定范围之内(默认10分钟),则并发连接数设置为0,并返回0
    } else if (currentTime - lastActiveRequestsCountChangeTimestamp > activeRequestsCountTimeout.get() * 1000 || count < 0) {
        activeRequestsCount.set(0);
        return 0;            
    } else {
    // 正常场景下返回并发连接数
        return count;
    }
}

AtomicInteger activeRequestsCount = new AtomicInteger(0);

private static final DynamicIntProperty activeRequestsCountTimeout = 
        DynamicPropertyFactory.getInstance().getIntProperty("niws.loadbalancer.serverStats.activeRequestsCount.effectiveWindowSeconds", 60 * 10);

关键点在于实例的并发数是如何维护的?下面我就接着看。

4、实例并发数的维护:

1)增加实例的并发数

在开始执行一个Rest请求时会通过ServerStats#incrementActiveRequestsCount()方法新增一个连接数(activeRequestsCount);
在这里插入图片描述

虽然是在new一个RibbonStatsRecorder时新增的实例并发数,但是RibbonStatsRecorder内部组合的ServerStats来源于Ribbo的上下文RibbonLoadBalancerContext,所以每次new RibbonStatsRecorder时,ServerStats数据是共享的;

在这里插入图片描述

2)减少实例的并发数

当Rest请求调用外部服务执行完毕之后,会通过ServerStats#decrementActiveRequestsCount()方法减少一个连接数(activeRequestsCount):
在这里插入图片描述

RibbonStatsRecorder#recordStats(Object entity) 方法如下:

在这里插入图片描述

三、后续文章

下一篇文章我们接着分析Ribbon负载均衡策略之WeightedResponseTimeRule。

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
1月前
|
负载均衡 IDE Java
SpringBoot整合XXL-JOB【04】- 以GLUE模式运行与执行器负载均衡策略
在本节中,我们将介绍XXL-JOB的GLUE模式和集群模式下的路由策略。GLUE模式允许直接在线上改造方法为定时任务,无需重新部署。通过一个测试方法,展示了如何在调度中心配置并使用GLUE模式执行定时任务。接着,我们探讨了多实例环境下的负载均衡策略,确保任务不会重复执行,并可通过修改路由策略(如轮训)实现任务在多个实例间的均衡分配。最后,总结了GLUE模式和负载均衡策略的应用,帮助读者更深入理解XXL-JOB的使用。
47 9
|
1月前
|
负载均衡 算法
架构学习:7种负载均衡算法策略
四层负载均衡包括数据链路层、网络层和应用层负载均衡。数据链路层通过修改MAC地址转发帧;网络层通过改变IP地址实现数据包转发;应用层有多种策略,如轮循、权重轮循、随机、权重随机、一致性哈希、响应速度和最少连接数均衡,确保请求合理分配到服务器,提升性能与稳定性。
248 11
架构学习:7种负载均衡算法策略
|
1月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
2月前
|
Cloud Native API 持续交付
云原生架构下的微服务治理策略与实践####
本文旨在探讨云原生环境下微服务架构的治理策略,通过分析当前面临的挑战,提出一系列实用的解决方案。我们将深入讨论如何利用容器化、服务网格(Service Mesh)等先进技术手段,提升微服务系统的可管理性、可扩展性和容错能力。此外,还将分享一些来自一线项目的经验教训,帮助读者更好地理解和应用这些理论到实际工作中去。 ####
72 0
|
2月前
|
负载均衡 Java Nacos
常见的Ribbon/Spring LoadBalancer的负载均衡策略
自SpringCloud 2020版起,Ribbon被弃用,转而使用Spring Cloud LoadBalancer。Ribbon支持轮询、随机、加权响应时间和重试等负载均衡策略;而Spring Cloud LoadBalancer则提供轮询、随机及Nacos负载均衡策略,基于Reactor实现,更高效灵活。
177 0
|
2月前
|
算法 NoSQL Java
微服务架构下的接口限流策略与实践#### 一、
本文旨在探讨微服务架构下,面对高并发请求时如何有效实施接口限流策略,以保障系统稳定性和服务质量。不同于传统的摘要概述,本文将从实际应用场景出发,深入剖析几种主流的限流算法(如令牌桶、漏桶及固定窗口计数器等),通过对比分析它们的优缺点,并结合具体案例,展示如何在Spring Cloud Gateway中集成自定义限流方案,实现动态限流规则调整,为读者提供一套可落地的实践指南。 #### 二、
86 3
|
2月前
|
监控 安全 持续交付
构建高效微服务架构:策略与实践####
在数字化转型的浪潮中,微服务架构凭借其高度解耦、灵活扩展和易于维护的特点,成为现代企业应用开发的首选。本文深入探讨了构建高效微服务架构的关键策略与实战经验,从服务拆分的艺术到通信机制的选择,再到容器化部署与持续集成/持续部署(CI/CD)的实践,旨在为开发者提供一套全面的微服务设计与实现指南。通过具体案例分析,揭示如何避免常见陷阱,优化系统性能,确保系统的高可用性与可扩展性,助力企业在复杂多变的市场环境中保持竞争力。 ####
64 2
|
3月前
|
消息中间件 运维 Cloud Native
云原生架构下的微服务优化策略####
本文深入探讨了云原生环境下微服务架构的优化路径,针对服务拆分、通信效率、资源管理及自动化运维等核心环节提出了具体的优化策略。通过案例分析与最佳实践分享,旨在为开发者提供一套系统性的解决方案,以应对日益复杂的业务需求和快速变化的技术挑战,助力企业在云端实现更高效、更稳定的服务部署与运营。 ####
|
3月前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
76 5
|
3月前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型浪潮中,企业纷纷拥抱云计算,而云原生架构作为其核心技术支撑,正引领着一场深刻的技术变革。本文聚焦于云原生环境下微服务架构的治理策略与实践,探讨如何通过精细化的服务管理、动态的流量调度、高效的故障恢复机制以及持续的监控优化,构建弹性、可靠且易于维护的分布式系统。我们将深入剖析微服务治理的核心要素,结合具体案例,揭示其在提升系统稳定性、扩展性和敏捷性方面的关键作用,为读者提供一套切实可行的云原生微服务治理指南。 ####