Spring Cloud LoadBalancer 详解:客户端负载均衡的原理与实践
- 一、负载均衡概述
- [二、Spring Cloud LoadBalancer 简介](#二、Spring Cloud LoadBalancer 简介)
-
- [⭐ LoadBalancer vs Ribbon(面试高频)](#⭐ LoadBalancer vs Ribbon(面试高频))
- 三、工作原理:从源码视角看一次调用
- [四、在项目中使用 Spring Cloud LoadBalancer](#四、在项目中使用 Spring Cloud LoadBalancer)
-
-
- [4.1 添加依赖](#4.1 添加依赖)
- [4.2 启用负载均衡的 RestTemplate](#4.2 启用负载均衡的 RestTemplate)
- [4.3 使用 WebClient(反应式)](#4.3 使用 WebClient(反应式))
- [4.4 负载均衡算法配置](#4.4 负载均衡算法配置)
-
- 五、负载均衡算法
-
-
- [5.1 开启服务发现](#5.1 开启服务发现)
- [5.2 同集群优先访问](#5.2 同集群优先访问)
- [⭐ 关于"权重"的重要说明(容易踩坑)](#⭐ 关于“权重”的重要说明(容易踩坑))
-
- [六、负载均衡 + 重试机制(关键补充)](#六、负载均衡 + 重试机制(关键补充))
- 七、与注册中心集成(增强说明)
-
- [⭐ 架构关系图(建议记住)](#⭐ 架构关系图(建议记住))
- 八、生产环境常见问题(重点加分)
-
- [1️⃣ 实例雪崩(热点实例)](#1️⃣ 实例雪崩(热点实例))
- [2️⃣ 脏实例问题](#2️⃣ 脏实例问题)
- [3️⃣ 跨机房调用](#3️⃣ 跨机房调用)
- [4️⃣ 冷启动问题](#4️⃣ 冷启动问题)
- 九、常见问题(补充增强)
-
- [9.1 LoadBalancer 不生效](#9.1 LoadBalancer 不生效)
- [9.2 实例为空](#9.2 实例为空)
- [9.3 自定义负载均衡注意点(补充)](#9.3 自定义负载均衡注意点(补充))
- 十、总结
-
-
- [1️⃣ 解耦设计](#1️⃣ 解耦设计)
- [2️⃣ 响应式支持](#2️⃣ 响应式支持)
- [3️⃣ 高扩展性](#3️⃣ 高扩展性)
-
在微服务架构中,一个服务通常会部署多个实例来实现高可用和水平扩展。当服务消费者需要调用这些实例时,如何选择其中一个进行请求?这便是负载均衡 要解决的问题。本文将深入介绍 Spring Cloud 官方推荐的客户端负载均衡器------Spring Cloud LoadBalancer,并解析其工作原理、设计思想、使用方法以及与 Nacos 等注册中心的集成。
一、负载均衡概述
负载均衡分为两类:
-
服务端负载均衡:通过独立的负载均衡器(如 Nginx、F5)将请求分发到后端服务器。
-
客户端负载均衡:服务消费者自己维护服务实例列表,并通过内置的负载均衡算法选择一个实例发起调用。Spring Cloud LoadBalancer 属于客户端负载均衡。
为什么越来越多系统选择客户端负载均衡?
-
去中心化,避免单点瓶颈
-
减少一次网络跳转(性能更高)
-
更灵活(可以按调用方定制策略)
二、Spring Cloud LoadBalancer 简介
Spring Cloud LoadBalancer 是 Spring Cloud 官方提供的客户端负载均衡器,用于替代已进入维护状态的 Netflix Ribbon。它轻量、易于扩展,并且与 Spring Cloud 生态无缝集成,支持两种工作模式:
-
反应式(Reactive):配合 WebClient 使用
-
阻塞式(Blocking):配合 RestTemplate 使用
⭐ LoadBalancer vs Ribbon(面试高频)
| 维度 | Ribbon | LoadBalancer |
|---|---|---|
| 状态 | 停止维护 | 官方推荐 |
| 模型 | 阻塞 | 支持响应式 |
| 架构 | 紧耦合 | 解耦设计 |
| 扩展方式 | Rule 类 | Supplier + Reactor |
👉 本质区别:
LoadBalancer 是"组件解耦 + 响应式重构"的新一代实现
三、工作原理:从源码视角看一次调用
我们以 lqb-user 调用 lqb-bank 为例。
3.1 核心组件(增强版)
-
LoadBalancerInterceptor :拦截请求,实现
ClientHttpRequestInterceptor,负责拦截经过RestTemplate的 HTTP 请求。 -
LoadBalancerClient:统一调用入口,负载均衡客户端,核心接口,负责根据服务名选择实例。
-
ServiceInstanceListSupplier:提供实例列表
-
ReactorLoadBalancer:负载均衡算法,具体负载均衡算法实现,如轮询、随机等。
⭐ 为什么要这样分层(非常重要)
text
职责拆分如下:
- Interceptor:解耦 HTTP 客户端
- Client:统一入口
- Supplier:解耦服务发现(可接 Nacos/Eureka/缓存)
- LoadBalancer:专注算法
👉 本质:
高内聚 + 低耦合 + SPI 可扩展设计
3.2 执行流程
-
核心流程:
RestTemplate
↓
LoadBalancerInterceptor
↓
LoadBalancerClient
↓
ServiceInstanceListSupplier(可能带缓存)
↓
ReactorLoadBalancer(选择实例)
↓
真实 HTTP 请求

- LoadBalancerInterceptor 做了什么? 它会拦截 HTTP 请求,从 URL 中提取服务名(host),然后调用 LoadBalancerClient 来选择具体实例,并替换请求地址。
- 服务实例是怎么拿到的? 通过 ServiceInstanceListSupplier 获取,通常是从注册中心(如 Nacos、Eureka)拉取。
- 实际上中间通常会有缓存层,例如 CachingServiceInstanceListSupplier,避免每次都访问注册中心。
-
概略:
用户请求 → RestTemplate → LoadBalancerInterceptor
↓
LoadBalancerInterceptor#intercept()
↓
解析 URL,提取服务名(例如 lqb-bank)
↓
调用 LoadBalancerClient#execute() 或 #choose()
↓
通过 ServiceInstanceListSupplier 获取服务实例列表(通常从注册中心如 Nacos 获取)
↓
ReactorLoadBalancer 根据算法选择一个实例
↓
构建真实请求 URI(http://ip:port/path),发起调用
步骤详解:
-
请求拦截 :当使用
@LoadBalanced注解的RestTemplate发送请求时,LoadBalancerInterceptor会拦截该请求。java// 伪代码示意 public ClientHttpResponse intercept(HttpRequest request, ...) { URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); // 从 URI 中获取服务名 return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, ...)); } -
获取服务实例 :
LoadBalancerClient通过ServiceInstanceListSupplier获取服务实例列表。ServiceInstanceListSupplier可以从多种来源获取实例,最常用的是从注册中心(如 Nacos、Eureka)获取。 -
负载均衡选择 :
ReactorLoadBalancer根据配置的规则(如轮询、随机、权重)从实例列表中选择一个实例。默认实现是RoundRobinLoadBalancer(轮询)。 -
发起实际请求 :将原始请求的 URI 中的服务名替换为选中的实例 IP 和端口,然后通过
RestTemplate发起真实 HTTP 请求。
整个过程完全透明,开发者只需关注业务代码。
⭐ 实例列表是否每次都从注册中心获取?
不是 ❗
默认会经过缓存层:
DiscoveryClientServiceInstanceListSupplier
↓
CachingServiceInstanceListSupplier(Caffeine)
优点:
-
降低注册中心压力
-
提升性能
风险:
- 短时间内可能使用"脏实例"
3.3 Reactive 模式的本质区别
Reactive 不只是换了 WebClient,而是:
java
Mono<Response<ServiceInstance>>
👉 实例选择本身也是异步的
优势:
-
无阻塞
-
更适合高并发
四、在项目中使用 Spring Cloud LoadBalancer
4.1 添加依赖
在 Spring Boot 3.x / Spring Cloud 2023.x 项目中,只需引入负载均衡器 Starter(通常通过其他依赖传递引入,但也可以显式添加):
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
如果你的项目已经使用了 Nacos 服务发现,spring-cloud-starter-alibaba-nacos-discovery 已经传递引入了 spring-cloud-starter-loadbalancer,无需重复添加。
4.2 启用负载均衡的 RestTemplate
在配置类中定义 RestTemplate Bean,并添加 @LoadBalanced 注解:
java
@Configuration
public class AppConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
之后,在代码中就可以直接使用服务名进行调用:
java
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/call-bank/{id}")
public String callBank(@PathVariable Long id) {
String url = "http://lqb-bank/api/bank/" + id; // 服务名 lqb-bank
return restTemplate.getForObject(url, String.class);
}
}
4.3 使用 WebClient(反应式)
若使用 WebClient,则需添加 @LoadBalanced 的 WebClient.Builder Bean:
java
@Configuration
public class WebClientConfig {
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
然后在业务代码中注入并使用:
java
@Service
public class BankService {
private final WebClient webClient;
public BankService(@LoadBalanced WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.build();
}
public Mono<String> getBankData(Long id) {
return webClient.get()
.uri("http://lqb-bank/api/bank/" + id)
.retrieve()
.bodyToMono(String.class);
}
}
4.4 负载均衡算法配置
默认负载均衡算法是轮询(RoundRobinLoadBalancer)。可以通过以下方式自定义:
-
全局配置 :在
application.yml中设置:yamlspring: cloud: loadbalancer: configurations: random # 可选:random, round-robin, retry, 或自定义但目前官方提供的枚举值有限,更推荐通过
@Bean方式自定义。 -
自定义负载均衡规则 :实现
ReactorServiceInstanceLoadBalancer接口,并注册为 Bean。例如,自定义一个基于权重的负载均衡器(可与 Nacos 权重配合)。-
创建自定义负载均衡器类:
javapublic class WeightedLoadBalancer implements ReactorServiceInstanceLoadBalancer { private final String serviceId; private final ObjectProvider<ServiceInstanceListSupplier> supplierProvider; public WeightedLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> supplierProvider, String serviceId) { this.supplierProvider = supplierProvider; this.serviceId = serviceId; } @Override public Mono<Response<ServiceInstance>> choose(Request request) { // 从 supplierProvider 获取实例列表,根据权重选择实例 // 具体实现略... } } -
通过配置类提供
ReactorLoadBalancer<ServiceInstance>Bean:java@Configuration public class LoadBalancerConfig { @Bean public ReactorLoadBalancer<ServiceInstance> weightedLoadBalancer( Environment env, LoadBalancerClientFactory loadBalancerClientFactory) { String serviceId = env.getProperty("loadbalancer.client.name"); return new WeightedLoadBalancer( loadBalancerClientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class), serviceId); } } -
在需要应用该规则的客户端指定配置(通过
@LoadBalancerClient或全局配置)。
-
-
结合 Nacos 权重:如果使用 Nacos,可以启用 Nacos 负载均衡扩展,使其自动利用 Nacos 实例的权重配置。
yamlspring: cloud: loadbalancer: nacos: enabled: true启用后,Nacos 客户端会将实例权重传递给 LoadBalancer,权重越高的实例被选中的概率越大。
五、负载均衡算法
默认:RoundRobinLoadBalancer 轮询算法
Spring Cloud LoadBalancer 本身不负责服务发现,它通过 ServiceInstanceListSupplier 获取实例列表。我们通常结合具体的注册中心(如 Nacos、Eureka)来实现:
- Nacos :
spring-cloud-starter-alibaba-nacos-discovery会自动配置一个NacosServiceInstanceListSupplier,它从 Nacos 获取服务实例列表。 - Eureka :类似的,
spring-cloud-starter-netflix-eureka-client也提供了对应的 Supplier。
这种设计将服务发现与负载均衡解耦,便于扩展。
5.1 开启服务发现
确保在启动类上添加 @EnableDiscoveryClient(虽然新版中可选),并且配置了正确的注册中心地址。以 Nacos 为例:
yaml
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: your-namespace-id
group: your-group
启动后,ServiceInstanceListSupplier 即可从 Nacos 拉取服务实例。
5.2 同集群优先访问
结合 Nacos 的集群配置,可以实现优先访问同机房实例。首先为服务配置集群名称:
yaml
spring:
cloud:
nacos:
discovery:
cluster-name: BJ
然后启用 Nacos 负载均衡扩展:
yaml
spring:
cloud:
loadbalancer:
nacos:
enabled: true
此时,LoadBalancer 会优先选择相同集群的实例,如果同集群无可用实例,再跨集群选择。这能有效降低跨机房调用延迟。
⭐ 关于"权重"的重要说明(容易踩坑)
❗默认不支持权重!
只有以下情况才支持:
- 开启 Nacos 扩展
- 或自定义实现
yaml
spring:
cloud:
loadbalancer:
nacos:
enabled: true
六、负载均衡 + 重试机制(关键补充)
默认情况下:
❗LoadBalancer 只负责选实例,不负责重试
如何实现重试?
方式一:
- 引入
spring-retry
方式二:
- 使用 WebClient retry
常见策略:
-
同实例重试
-
切换实例重试(推荐)
⚠️ 注意
非幂等接口(如 POST)慎用重试!
七、与注册中心集成(增强说明)
LoadBalancer 本身不负责服务发现,而是通过:
text
ServiceInstanceListSupplier
实现解耦。
⭐ 架构关系图(建议记住)
RestTemplate/WebClient
↓
LoadBalancerInterceptor
↓
LoadBalancerClient
↓
ServiceInstanceListSupplier(可缓存)
↓
ReactorLoadBalancer
↓
ServiceInstance
八、生产环境常见问题(重点加分)
1️⃣ 实例雪崩(热点实例)
问题:
- 某个实例被频繁选中
解决:
- 随机策略
- 权重分散
2️⃣ 脏实例问题
问题:
- 实例已下线,但仍被调用
原因:
- 本地缓存未更新
解决:
- 缩短缓存时间
- 健康检查
3️⃣ 跨机房调用
问题:
- 延迟高
解决:
- Nacos cluster(同集群优先)
4️⃣ 冷启动问题
问题:
- 新实例刚上线被打满
解决:
- 预热(warm-up)
- 限流
九、常见问题(补充增强)
9.1 LoadBalancer 不生效
- 未加
@LoadBalanced - URL 写成 IP
- 依赖缺失
9.2 实例为空
- 注册中心配置错误
- namespace/group 不一致
9.3 自定义负载均衡注意点(补充)
java
String serviceId = env.getProperty("loadbalancer.client.name");
👉 说明:
该 serviceId 由 LoadBalancerClientFactory 在运行时注入,表示当前服务名。
十、总结
Spring Cloud LoadBalancer 的核心价值在于:
1️⃣ 解耦设计
-
服务发现(Supplier)
-
负载均衡(Reactor)
-
调用入口(Client)
2️⃣ 响应式支持
-
实例选择异步化
-
更适合高并发
3️⃣ 高扩展性
-
可自定义算法
-
可接入不同注册中心
-
支持缓存与扩展机制
👉 一句话总结:
Spring Cloud LoadBalancer = 可插拔的客户端负载均衡框架(解耦 + 响应式 + 可扩展)
Spring Cloud LoadBalancer 是一个客户端负载均衡器,通过拦截请求,在本地缓存的实例列表中,使用负载均衡算法选择服务实例,并支持响应式和高度扩展的架构设计。