[toc]
| 单词 | 词性 | 音标 | 含义 |
|---|---|---|---|
| load balancer | n. | /ləʊd ˈbælənsə(r)/ | 负载均衡(把请求分散到多个实例,避免某个实例过载) |
| strategy | n. | /ˈstrætədʒi/ | 策略(负载均衡选实例的规则:轮询、随机、权重…) |
| weight | n./v. | /weɪt/ | 权重(按比例分配流量的依据,值越大分到越多) |
| round-robin | adj. | /raʊnd ˈrɒbɪn/ | 轮询(轮流把请求分给每个实例) |
| sticky | adj. | /ˈstɪki/ | 粘性的(同一请求尽量固定到同一实例,便于保持会话) |
| degrade | v. | /dɪˈɡreɪd/ | 降级(某实例不可用时,自动剔除并继续用其他实例) |
Lecture06-SpringCloud-负载均衡LoadBalancer
目标:理解客户端负载均衡原理,掌握 Spring Cloud LoadBalancer 的内置策略与指定方式(轮询 / 随机 / Nacos 权重),以及如何给 Feign 调用分配多个实例。
1、什么是负载均衡
同一个服务如果只有一个实例,那么一旦宕机就无法对外提供服务,所以基本上都需要搭建集群,配套多个实例,避免单点故障。
另外,搭建集群还能够让各个实例分摊负载,避免单个实例被海量请求压垮。
那么对消费端来说,每一次请求都需要决定具体访问哪一个实例。
在注册中心有详细的实例列表,由负载均衡组件决定具体调用哪一个。
OpenFeign 需要做的是给负载均衡组件提供目标服务的名称。
user-service 有 3 个实例:127.0.0.1:8081 / 8082 / 8083
调用方每次发起 /user/{id} 请求,负载均衡选一个实例
两种负载均衡:
- 服务端:由独立负载均衡器(如 Nginx)分发,客户端只连一个入口。
- 客户端(本课程):调用方内置选实例逻辑(Spring Cloud LoadBalancer),配合 Nacos 发现实例。
衔接:第 03 讲我们手写"取第一个实例",第 05 讲 OpenFeign 默认就用了 LoadBalancer——这就是本讲要讲清楚的那层逻辑。
2、Ribbon 已退役,用 LoadBalancer
早期 Spring Cloud 用 Netflix Ribbon 做客户端负载均衡,已停止维护。现用官方 Spring Cloud LoadBalancer。
⚠️ 它不是 OpenFeign 自动带进来的。官方文档明确写:
spring-cloud-starter-openfeign把spring-cloud-starter-loadbalancer列为 optional(可选) 依赖,不会传递给你。不加它会怎样?Feign 调
@FeignClient(name="user-service")时,需要按"服务名"解析出实例地址——这一步就是 LoadBalancer 在做的。缺了它,启动或运行时会报找不到FeignBlockingLoadBalancerClient的错。所以只要用 Feign 调远程服务,这一行就得自己加:
<!-- 必须显式加:openfeign 里它是 optional,不自动传递 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
3、默认负载均衡规则
LoadBalancer 默认策略是 轮询(Round-Robin):多个实例轮流被选中,请求被均分。
演示方式:给 user-service 开 3 个实例(8081/8082/8083,同一 jar 不同端口),用 Feign 连续调用多次,观察请求分别落到不同实例。
第 1 次请求 → 8081
第 2 次请求 → 8082
第 3 次请求 → 8083
第 4 次请求 → 8081 (回到起点,循环)
验证手段:每个实例返回内容里带自己的端口。
4、内置策略有哪些
LoadBalancer 的选择逻辑分两层,先分清这两层,后面就不会混淆:
| 层次 | 负责什么 | 接口 |
|---|---|---|
| 算法层 | 从候选实例里"挑"哪一个 | ReactorLoadBalancer |
| 筛选层 | 先决定"哪些实例进入候选" | ServiceInstanceListSupplier |
算法层:官方只提供了两个实现
| 策略 | 实现类 | 说明 |
|---|---|---|
| 轮询(默认) | RoundRobinLoadBalancer | 多个实例轮流被选中,请求被均分 |
| 随机 | RandomLoadBalancer | 每次从候选实例里随机挑一个 |
筛选层:官方提供了多个开箱即用的预置配置(用配置属性切换,见下一节)
| 预置值 | 效果 |
|---|---|
default(默认) | 只做基础发现 + 缓存 |
weighted | 加权,按实例 metadata 里的 weight 值分配流量 |
same-instance-preference | 尽量复用上一次选中的实例(替代 Zookeeper 的 StickyRule) |
request-based-sticky-session | 按请求 cookie 固定到同一实例 |
zone-preference | 同区域(zone)的实例优先 |
health-check | 定时探活,只返回健康实例 |
subset | 只在一部分实例子集里选(压测、灰度常用) |
api-version | 按接口版本筛选实例 |
另外,Spring Cloud Alibaba 还提供了
NacosLoadBalancer:它读的是 Nacos 控制台上的实例权重,并且同集群实例优先。它不属于 Spring Cloud 官方内置,需要单独开启。
5、如何指定策略
1、方式一:配置类切换算法(改"挑"的规则)
RoundRobinLoadBalancer / RandomLoadBalancer 不能用配置文件切换(原因见本节的"常见误区"),要写配置类 + 注解。
第 1 步:写配置类。注意:不要加 @Configuration,也不要放进主启动类的组件扫描范围。
// src/main/java/com/atguigu/cloud/lb/CustomLoadBalancerConfiguration.java
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientFactory;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
// 当前正在为哪个服务做负载均衡(这里是 user-service)
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
第 2 步:挂到目标服务上
// 只对 user-service 生效
@Configuration
@LoadBalancerClient(value = "user-service", configuration = CustomLoadBalancerConfiguration.class)
public class LbClientConfig {
}
想让所有服务都用随机,改用 @LoadBalancerClients:
// 对所有服务生效
@Configuration
@LoadBalancerClients(defaultConfiguration = CustomLoadBalancerConfiguration.class)
public class LbGlobalConfig {
}
| 注解 | 作用范围 |
|---|---|
@LoadBalancerClient(value = "服务名", configuration = X.class) | 只对该服务生效 |
@LoadBalancerClients(defaultConfiguration = X.class) | 全局默认 |
2、方式二:配置属性切换预置配置(改"筛"的规则)
筛选层的预置配置直接写配置文件即可,还能细到某个服务:
spring:
cloud:
loadbalancer:
configurations: weighted # 全局:所有服务都按权重筛选
# clients:
# user-service:
# configurations: health-check # 只对 user-service 生效(按需开启)
3、方式三:Nacos 权重(Spring Cloud Alibaba)
生产上更常用的是"按机器性能分配流量"——新机器权重低一点,让它先"热身"。用 SCA 的 NacosLoadBalancer 实现:
spring:
cloud:
loadbalancer:
nacos:
enabled: true # 开启 NacosLoadBalancer(默认 false,即默认仍走轮询)
nacos:
discovery:
weight: 1 # 本实例权重,1~100,越大分到的流量越多
验证方式:把 user-service 起两个实例,权重分别设成 1 和 100,连续调用 20 次,观察绝大多数请求落到高权重实例上。
# 实例 A(权重 1)
java -jar user-service.jar --server.port=8081 --spring.cloud.nacos.discovery.weight=1
# 实例 B(权重 100)
java -jar user-service.jar --server.port=8082 --spring.cloud.nacos.discovery.weight=100
4、常见误区:configurations=random 是错的
网上不少文章写"把策略改成随机,只要配 spring.cloud.loadbalancer.configurations=random"——这个值根本不存在。
查 LoadBalancerClientConfiguration 源码可知,configurations 只认这几个值:default、zone-preference、health-check、request-based-sticky-session、same-instance-preference、weighted、subset、api-version。
原因正是前面那个两层结构:configurations 管的是筛选层(哪些实例进候选),而轮询/随机属于算法层,只能用配置类 + @LoadBalancerClient 替换。写错了不会报错,只会静默地继续用轮询——这正是它容易骗过人的地方。
6、实例剔除与容错
负载均衡会自动感知实例健康状态:
- 通过 Nacos 心跳,不健康的实例不会出现在候选列表里。
- 某个实例调用失败,LoadBalancer 可结合重试换下一个实例再试。
spring:
cloud:
loadbalancer:
cache:
enabled: true # 缓存实例列表,减少查询压力
进阶:
spring.cloud.loadbalancer.retry配合超时,实现"失败自动重试其他实例"(可让 Feign + LoadBalancer 一起配置重试,生产常用)。
7、课堂练习
- 把
user-service用不同端口开 3 个实例,让order-service通过 Feign 连续调用 10 次。 - 观察轮询效果:请求是否被平均分到 3 个实例。
- 用配置类 +
@LoadBalancerClient把策略换成RandomLoadBalancer,再调用 10 次,对比两次的分布差异。 - 给两个实例设置不同权重(
1和100),并开启spring.cloud.loadbalancer.nacos.enabled=true,观察流量是否向高权重实例倾斜。 - 停掉其中一个实例,观察剩余实例是否仍能正常响应(健康剔除)。
8、本讲小结
- 负载均衡解决"选哪个实例",客户端负载均衡由调用方内置。
- Ribbon 退役,用 Spring Cloud LoadBalancer(必须自己显式加依赖——openfeign 把它列为 optional,不自动传递)。
- 选择逻辑分两层:算法层(
ReactorLoadBalancer:轮询 / 随机)与筛选层(ServiceInstanceListSupplier:权重 / 同实例优先 / 健康检查…)。 - 算法层用配置类 +
@LoadBalancerClient替换;筛选层用spring.cloud.loadbalancer.configurations属性切换。 - SCA 额外提供 NacosLoadBalancer(
spring.cloud.loadbalancer.nacos.enabled=true),按 Nacos 权重 + 同集群优先。 - 不健康实例自动剔除,结合重试实现容错。
9、本节验证
- 理解负载均衡的作用与常见策略(轮询/随机/权重/粘性)✅
- 知道 Ribbon 已退役,用 Spring Cloud LoadBalancer,且它需要自己显式引入(openfeign 不传递)✅
- 说得清算法层与筛选层的分工,会用配置类把轮询换成随机 ✅
- 会切换预置配置(
configurations)并用 Nacos 权重做流量倾斜 ✅ - 知道
configurations=random是误传,写了不会生效 ✅ - 理解实例剔除与容错降级 ✅