type
status
date
slug
summary
tags
category
icon
password

1、什么是 LoadBalancer ?

LoadBalancer(负载均衡器)是一种用来分发网络或应用程序流量到多个服务器的技术。它可以防止任何单一服务的过载,通过分散负载来保持整个系统的平稳运行,保证系统的高可用性和可靠性。

2、负载均衡策略的分类

2.1 均衡负载策略分类

负载均衡策略大体上分为两类:服务端的负载均衡和客户端的负载均衡
  • 服务端负载均衡 (如 Nginx、F5)
请求先到达一个中介(如负载均衡器设备或者服务,例如Nginx),由这个中介根据配置的策略将请求分发到后端的多个服务器中。它对客户端是透明的,即客户端不需要知道有多少服务器以及它们的存在。
notion image
  • 客户端负载均衡 (如 Ribbon、Spring Cloud LoadBalancer)
请求的分配逻辑由客户端持有,客户端直接决定将请求发送到哪一个服务器。也就是说在客户端负载均衡中,客户端通常具备一份服务列表,它知道每个服务的健康状况,基于这些信息和负载均衡策略,客户端会选择一个最适合的服务去发送请求。
notion image
服务端负载均衡和客户端负载均衡策略有什么区别 ?
它俩的区别主要在灵活性和性能两方面(结合上面两幅图来理解):
1. 灵活性
  • 客户端负载均衡更加灵活,它可以针对每一个请求,每一个 service 做单独的负载均衡配置。
2. 性能
  • 客户端负载均衡性能相对来说更好一点,因为服务端负载均衡中,当请求来了之后,它得先去到服务端的负载均衡,然后服务端的负载均衡再将请求发送给对应的服务器,整个过程发送了两次请求,而客户端负载均衡只需要发送一次请求。
  • 其次,服务端负载均衡中,客户端的请求都先打到了中心节点上,这个流量是很大的,所以服务端的负载均衡器,它的压力相对来说就比较大,那么性能就不可能比客户端负载均衡高。
  • 反观客户端负载均衡,它就没有所谓的中心节点,它将集中的压力给释放了,因为客户端有成千上万个,它可以让每个客户端去调用自己的负载均衡器,而不是让成千上万个客户端去调用一个负载均衡器。
【扩充知识】
如果将负载均衡器视为代理,那么服务端负载均衡可以视作是反向代理的一种形式,因为它接收客户端请求后再决定将其分配给哪一个服务器;而客户端负载均衡则可以看作具有正向代理的性质,因为客户端知道要联系的服务列表,并直接向选定的服务器发送请求。
  • 正向代理:正向代理类似于一个中间人,代表客户端去请求服务。客户端必须要配置代理,因此客户端是知道代理的存在的。正向代理隐藏了客户端的信息,服务器不知道真正的请求者是谁。
  • 反向代理:反向代理则是代表服务器接收客户端的请求。客户端通常不知道后面有多少服务器,也不需要知道。反向代理隐藏了服务端的信息,客户端只与反向代理交互,像Nginx这样的服务器就是一个反向代理的例子。

2.2 常见的负载均衡策略

常见的负载均衡策略有以下几种:
  1. 轮询:按顺序分配,每个服务器轮流接收一个连接。
  1. 随机选择:随机挑选服务器,分散负载。
  1. 最少连接:选择当前连接数最少的服务器。
  1. IP哈希:根据用户IP分配,相同IP的请求总是发给同一服务器。
  1. 加权轮询:类似轮询,但服务器根据权重获取更多或更少请求。
  1. 加权随机选择:权重高的服务器有更高几率被选中。
  1. 最短响应时间:响应时间短的服务器优先接收新请求。

3、为什么要学习 Spring Cloud Balancer ?

因为 Ribbon 作为早期的客户端负载均衡工具,在 Spring Cloud 2020.0.0 版本之后已经被移除了,取而代之的是 Spring Cloud LoadBalancer,而且 Ribbon 也已经不再维护,所以它也是 Spring 官方推荐的负载均衡解决方案。
  • 更好的兼容性:LoadBalancer就像一个全新的配件,它与Spring Cloud的其他组件搭配得更好。
  • 支持响应式编程:现在编程界有一种新的编程方式叫做“响应式编程”,LoadBalancer能很好地支持这种现代编程风格。
  • 易于使用和维护:LoadBalancer的设计易于拼装和修改,这对于开发者来说,维护和定制起来更加方便。
  • 多功能:LoadBalancer有很多内置功能,比如自动帮你挑选服务器,就像购物网站帮你推荐商品一样聪明。
Spring Cloud LoadBalancer 和 Ribbon 对比如下:
对比项
Ribbon
Spring Cloud LoadBalancer
维护状态
已停更,不推荐新项目使用
官方维护,推荐替代方案
编程模型
同步阻塞
响应式异步
配置复杂度
较高,需手动指定服务列表或依赖 Eureka 等注册中心
较低,与 Spring Cloud 深度集成。支持 @LoadBalanced 注解,与 RestTemplate 或 WebClient 无缝协作。
负载策略
多种(轮询、随机、加权等)
默认轮询和随机,支持自定义
性能
适合小型系统,随着实例数量增加,性能可能下降
高并发优化,扩展性更强
适用场景
传统 Spring MVC 项目,或需要兼容旧版 Spring Cloud 的应用
新项目,尤其是基于 Spring WebFlux 的响应式微服务

4、Spring Cloud LoadBalancer 的负载均衡策略

Spring Cloud LoadBalancer 内置了三种负载均衡策略:
  1. 轮询负载均衡策略,默认策略。
  1. 随机负载均衡策略。
  1. Nacos 均衡负载策略。
除此之外我们还可以自定义均衡负载策略

4.1 轮询负载均衡策略(默认的)

默认的负载均衡策略是轮询策略。配置类为 LoadBalancerClientConfiguration
进入到 RoundRobinLoadBalancer 这个类里边,定位到 getInstanceResponse 方法,就能看到轮询策略的关键代码:
理解关键代码:
  • this.position.incrementAndGet() 方法等价于 "++随机数 "。这是一个原子操作,保证了每次调用都会得到一个唯一的递增数值。
  • & Integer.MAX_VALUE 这部分是一个位运算,它确保了如果 position 的值增加到超过 Integer.MAX_VALUE 时,不会产生负数。其一,在轮询算法中,如果计数器变成负数,那么取余操作可能会产生负的索引值,这是无效的; 其二,也可也保证在相同规则底下的公平性。
  • instances 是一个包含所有服务实例的列表。
  • pos % instances.size() 计算的是 pos 除以 instances 列表大小的余数,这保证了不论 pos 增长到多大,这个表达式的结果都是在 0instances.size() - 1 的范围内,这样就可以循环地从服务实例列表中选择服务实例。

4.2 随机负载均衡策略

LoadBalancer 内置了随机负载策略,实现类为 RandomLoadBalancer ,启用随机负载均衡策略的步骤:
  1. 注入随机负载均衡策略的 Bean
  1. 设置随机负载均衡策略

4.2.1 注入随机负载均衡策略的bean

4.2.2 设置随机负载均衡策略

设置局部的均衡负载策略:在 consumer 模块中的 service 接口上设置负载均衡策略:
有时候局部的负载均衡策略不会生效(版本问题),可以将其设为全局的负载均衡策略。
设置全局的负载均衡策略:在启动类上加 @LoadBalancerClients 注解

4.3 Nacos 权重负载均衡器

Nacos 中有两种负载均衡策略:权重负载均衡策略和 CMDB(地域就近访问)标签负载均衡策略。默认的策略是权重。
在 Spring Cloud Balancer 配置为 Nacos 负载均衡器的步骤:
  1. 注入 Nacos 负载均衡器的 bean
  1. 设置 Nacos 负载均衡器

4.3.1 注入Nacos 负载均衡器的bean

配置 Nacos 负载均衡需要注入 NacosDiscoveryProperties 这个类,因为它需要使用到配置文件中的一些关键信息。

4.3.2 设置 Nacos 负载均衡器

可以设置局部的负载均衡策略或者全局的均衡负载策略。以全局的均衡负载策略为例:
再测试之前,可以先将 Nacos 中一个生产者的权重给设置为 10,一个设置为 1,这样就能明显感受到 Nacos 权重的负载均衡策略了。
notion image

4.4 自定义负载均衡器

自定义负载均衡策略需要 3 个步骤:
  1. 创建自定义负载均衡器
  1. 注入自定义负载均衡器的 bean
  1. 设置自定义负载均衡策器

4.4.1 创建自定义负载均衡器

这里也是可以参考源码的实现的,搜索 RandomLoadBalancer 这个类,模仿它的实现去创建自定义负载均衡器。
  • 创建一个负载均衡类, 并让其实现 ReactorServiceInstanceLoadBalancer 接口;
  • 复制 RandomLoadBalancer 的整个方法体,粘贴到自定义负载均衡类中,并修改构造方法名称
  • 在关键方法 getInstanceResponse 中实现自定义负载均衡策略(以IP哈希负载均衡为例)

4.4.2 注入自定义负载均衡器的bean

4.4.3 设置自定义负载均衡策器

可以设置局部的负载均衡策略或者全局的均衡负载策略。以全局的均衡负载策略为例:

4.5 多个均衡负载器的使用技巧

当我们有多个均衡负载器的实现类,可以借助 @ConditionalOnXXX 注解实现按需加载均衡负载策略。
然后在 application.yml 配置文件里面激活需要的均衡负载策略

5、Spring Cloud LoadBalancer 中的特性

5.1 缓存机制

Spring Cloud LoadBalancer 中获取服务实例有两种方式:
1. 实时获取:每次都从注册中心得到最新的健康实例(效果好,开销大)
2. 缓存服务列表:每次得到服务列表之后,缓存一段时间(既保证性能,也能保证一定的及时性)
Spring Cloud LoadBalancer 默认开启了缓存服务列表的功能。
测试 Spring Cloud LoadBalancer 的缓存机制:
  1. 将前面设置负载均衡策略全部注释掉,使用默认的轮询测试(便于观察)
  1. 准备两个服务
  1. 将其中一个服务下线,下线的同时立马去获取服务,然后等大约 35s ,再去获取服务
notion image
测试结果:当我下线第一个服务的时候,立马去获取服务,这个时候还是两个服务轮询的获取,等过了 35s 左右,就只能获取到 64067 这个服务了。
Spring Cloud LoadBalancer 中缓存服务列表的默认特性如下:
  • 缓存的过期时间为 35s。
  • 缓存保存个数为 256 个。
我们可以通过在配置文件中去设置这些特性:
也可以通过配置关闭缓存
尽管关闭缓存对于开发和测试很有用,但是在生产环境上,它的效率是要远低于开启缓存,所以在生产环境上始终都要开启缓存。

6、Spring Cloud LoadBalancer原理

OpenFeign 底层是通过 HTTP 客户端对象 RestTemplate 实现接口请求的,而负载均衡器的作用只是在请求客户端发送请求之前,得到一个服务的地址给到 RestTemplate 对象。 Spring Cloud LoadBalancer 的核心组件包括
  • ServiceInstanceListSupplier :负责从注册中心获取可用的服务实例列表。
  • ReactiveLoadBalancer:均衡负载策略。
  • LoadBalancerClient :根据负载均衡策略从可用的服务实例列表中选择一个合适的实例,然后向实例发起实际请求。

6.1 ServiceInstanceListSupplier

ServiceInstanceListSupplier 是一个接口,用于提供服务实例列表。它的实现类负责从服务注册中心(如 Nacos、Eureka、Consul 等)获取可用的服务实例列表。
Flux 是 Reactor 库中的一个类,表示一个异步序列。ServiceInstanceListSupplier 的 get 方法返回一个 Flux,它会异步地提供服务实例列表。
Spring Cloud 提供了多种内置实现,例如:
  • DiscoveryClientServiceInstanceListSupplier :基于服务发现客户端(如 Nacos、Eureka、Consul 等)获取实例列表。
  • ZonePreferenceServiceInstanceListSupplier :优先选择同区域的实例(AWS 可用区或其他区域划分)。
  • HealthCheckServiceInstanceListSupplier :过滤掉不健康的实例。
  • CachingServiceInstanceListSupplier :缓存实例列表以提高性能。

6.2 ReactiveLoadBalancer

ReactiveLoadBalancer 是负载均衡的核心接口,定义了负载均衡的策略。它的主要方法是 choose,用于根据负载均衡策略选择一个服务实例。
Spring Cloud提供了三种种主要的实现方式:
  • RoundRobinLoadBalancer:基于轮询算法的实现,是默认的策略
  • RandomLoadBalancer:基于随机选择算法的实现
  • NacosLoadBalancer:基于 Nacos 的均衡负载策略
notion image

6.3 LoadBalancerClient

LoadBalancerClient 是一个接口,定义了负载均衡客户端的基本操作。主要提供以下功能:
  1. 服务实例选择:从注册中心(如 Nacos、Eureka、Consul 等)获取可用的服务实例列表,并根据负载均衡策略选择一个合适的实例。
  1. 请求执行:将原始服务 ID(如 http://user-service)转换为实际的服务实例 URL(如 http://192.168.1.100:8080)并执行请求。

6.4 Spring Cloud LoadBalancer 均衡负载流程

  1. 服务实例获取阶段:
      • 通过 ServiceInstanceListSupplier 从注册中心(如 Nacos)获取服务实例列表
      • 缓存服务实例信息到本地,定期更新
  1. 请求拦截阶段:
      • LoadBalancerAutoConfiguration 在扫描到有被 @LoadBalanced 注解标记的 RestTemplate 或 WebClient 时生效。
      • 然后自动将 LoadBalancerInterceptor 添加到 RestTemplate 的拦截器链中。
  1. 实例选择阶段:
      • ReactiveLoadBalancer 使用负载均衡策略(如轮询、随机等)从服务实例列表中选择一个服务实例。
      • LoadBalancerClient 调用 ReactiveLoadBalancerchoose 方法,获取选择的服务实例。
  1. 请求执行阶段:
      • LoadBalancerClient 将原始请求中的服务名替换为选中的实际实例地址,并把请求到目标服务实例。

6.5 自定义均衡负载流程

1、RestTemplate配置使用随机的均衡负载策略。
2、Consumer 使用 resttemplate 向 Provider 的在服务注册和发现中心的服务 id 发起调用。
3、Provider 提供接口供 Consumer 调用。另外 Provider 在启动的时候加上 JVM 参数 -Dinstance=instanceX
4、多次发起 GET 请求调用 http://localhost:8000/consumer/hello-loadbalancer,返回结果如下:
Hexo+GitHub搭建个人博客教程Nacos系列:Nacos架构解析