文章目录
-
- 一、开篇:Ribbon的"退场"与新王的"登基"
- 二、Ribbon淘汰原因深度剖析
-
- [2.1 三大退役原因](#2.1 三大退役原因)
- [2.2 替代关系对照](#2.2 替代关系对照)
- 三、LoadBalancer核心架构解析
-
- [3.1 核心组件架构](#3.1 核心组件架构)
- [3.2 核心流程详解](#3.2 核心流程详解)
- [3.3 核心接口源码解析](#3.3 核心接口源码解析)
- 四、负载均衡算法深度剖析
-
- [4.1 默认策略:轮询(RoundRobin)](#4.1 默认策略:轮询(RoundRobin))
- [4.2 随机策略(Random)](#4.2 随机策略(Random))
- [4.3 加权策略(Weighted)](#4.3 加权策略(Weighted))
- [4.4 区域感知策略(Zone Preference)](#4.4 区域感知策略(Zone Preference))
- [4.5 同实例优先策略(Same Instance Preference)](#4.5 同实例优先策略(Same Instance Preference))
- [4.6 策略组合与优先级](#4.6 策略组合与优先级)
- 五、Nacos集成实战
-
- [5.1 依赖配置](#5.1 依赖配置)
- [5.2 启用LoadBalancer](#5.2 启用LoadBalancer)
- [5.3 验证负载均衡](#5.3 验证负载均衡)
- [5.4 Nacos感知的权重负载均衡](#5.4 Nacos感知的权重负载均衡)
- 六、2025版新特性
- 七、生产环境踩坑指南
- 八、课后作业
- 九、下节预告
- [🔗《最新版 SpringCloud 2025 从入门到实战》系列课程导航](#🔗《最新版 SpringCloud 2025 从入门到实战》系列课程导航)

适配版本 :Spring Cloud 2025.1.3(Oakwood)、Spring Boot 4.0.8、Spring Cloud Alibaba 2025.1.0.0、Nacos Server 3.1.1、JDK 21
课程定位:服务通信阶段开篇,从Ribbon淘汰讲到新版LoadBalancer架构,掌握负载均衡底层原理与算法实战
一、开篇:Ribbon的"退场"与新王的"登基"
第12课结束时,我们完成了配置中心阶段的全部内容。现在进入第四阶段:服务通信核心。这是微服务从"各自为政"走向"协作联动"的关键阶段。
在服务通信中,一个核心问题是:当服务A调用服务B时,服务B有多个实例,请求该发给谁?这就是负载均衡要解决的问题。
在Spring Cloud的早期版本中,这个问题的答案几乎只有一个:Netflix Ribbon。Ribbon是Netflix开源的客户端负载均衡组件,自2014年诞生以来就承担着Spring Cloud生态中客户端负载均衡的核心职责。它提供了丰富的功能:超时控制、重试机制、断路器模式、多种负载均衡策略(轮询、随机、可用性过滤等),在很长一段时间内是微服务负载均衡的"事实标准"。
但Ribbon的故事在2020年迎来了转折点。Spring Cloud 2020.0.0(Ilford)正式将Ribbon移除出核心依赖,取而代之的是Spring官方自研的Spring Cloud LoadBalancer。Ribbon的退役源于其与Netflix Eureka的紧密耦合------Eureka进入维护模式后,Ribbon也随之失去了支持,同时Ribbon不支持响应式编程,无法适配WebClient和Reactor技术栈。
Spring Cloud LoadBalancer(以下简称SCLB)作为替代者,从2017年就开始在Spring Cloud Incubator孵化器中开发,经过数年打磨最终成为官方标准。它不仅解决了Ribbon停更、不支持响应式编程的痛点,还采用了更轻量级的线程模型,在P99延迟指标上比Ribbon有15-20%的提升。
本课将从Ribbon淘汰原因讲到SCLB核心架构,从负载均衡算法源码讲到Nacos集成实战,从区域感知等高级特性讲到生产环境踩坑方案,完整覆盖新版负载均衡的所有关键点。
二、Ribbon淘汰原因深度剖析
2.1 三大退役原因
原因一:与Eureka的强绑定。Ribbon的核心依赖是Eureka提供的服务注册表信息。当Eureka进入维护模式后,Ribbon也失去了持续更新的基础。更关键的是,Ribbon的设计架构深度依赖Netflix的开源生态,无法独立演进。
原因二:不支持响应式编程。Spring Framework 5引入响应式编程模型后,WebClient、Reactor等技术栈成为微服务高并发场景的首选。Ribbon的线程模型是阻塞式的,无法适配响应式编程。Spring Cloud LoadBalancer天然适配WebClient和Reactor,支持反应式编程,满足高并发场景需求。
原因三:配置模型复杂 。Ribbon的配置模型较为复杂,大量参数需要调优(ribbon.ConnectTimeout、ribbon.ReadTimeout、ribbon.MaxAutoRetries等),且配置命名规则不够统一,给开发者带来了较高的学习成本。
2.2 替代关系对照
| 维度 | Ribbon(已淘汰) | Spring Cloud LoadBalancer |
|---|---|---|
| 响应式支持 | ❌ 不支持 | ✅ 原生支持WebClient/Reactor |
| 服务发现集成 | Eureka绑定 | 通用DiscoveryClient抽象 |
| 默认策略 | RoundRobinRule | RoundRobinLoadBalancer |
| 配置复杂度 | 高(参数众多) | 低(简洁配置) |
| 维护状态 | 已停止 | 官方活跃维护 |
| 扩展方式 | ILoadBalancer接口 | ReactorServiceInstanceLoadBalancer接口 |
核心结论 :新项目不应再使用Ribbon 。存量项目迁移到SCLB时,API层面的兼容性较好(@LoadBalanced注解仍然有效),主要变化在于配置项和自定义策略的实现方式。
三、LoadBalancer核心架构解析
3.1 核心组件架构
Spring Cloud LoadBalancer的架构设计非常清晰,由五个核心组件构成:
┌─────────────────────────────────────────────────────────────┐
│ LoadBalancerClient │
│ 客户端负载均衡统一入口 │
│ choose() / execute() / reconstructURI() │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ LoadBalancerClientFactory │
│ 为每个服务创建独立的LoadBalancer实例 │
│ 基于 Spring 父子容器隔离 │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ReactorServiceInstanceLoadBalancer │
│ 负载均衡策略选择器 │
│ RoundRobinLoadBalancer / RandomLoadBalancer / 自定义 │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ ServiceInstanceListSupplier │
│ 服务实例列表供应器 │
│ DiscoveryClientSupplier / Caching / HealthCheck / Zone │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ DiscoveryClient │
│ 从注册中心(Nacos/Consul/Eureka)获取实例列表 │
└─────────────────────────────────────────────────────────────┘
组件职责说明:
| 组件 | 职责 | 关键接口/类 |
|---|---|---|
LoadBalancerClient |
负载均衡的统一入口,封装选择实例和执行请求的逻辑 | BlockingLoadBalancerClient(阻塞式) / ReactiveLoadBalancer(响应式) |
LoadBalancerClientFactory |
为每个服务创建独立的LoadBalancer实例,基于Spring父子容器实现服务间的配置隔离 |
LoadBalancerClientFactory |
ReactorServiceInstanceLoadBalancer |
负载均衡策略的选择器,决定"选哪个实例" | RoundRobinLoadBalancer / RandomLoadBalancer |
ServiceInstanceListSupplier |
服务实例列表的供应器,决定"有哪些实例可选" | DiscoveryClientServiceInstanceListSupplier / CachingServiceInstanceListSupplier |
DiscoveryClient |
从注册中心获取实例列表的抽象层,支持Nacos、Consul等 | ReactorDiscoveryClient / DiscoveryClient |
3.2 核心流程详解
一次完整的负载均衡调用流程:
Consumer 发起请求
│
▼
LoadBalancerInterceptor 拦截请求(RestTemplate场景)
│ 从URL中提取服务名(如 "service-user")
▼
LoadBalancerClient.choose(serviceId)
│
▼
LoadBalancerClientFactory 获取该服务的LoadBalancer实例
│
▼
ServiceInstanceListSupplier.get() 获取实例列表
│ 从本地缓存获取(缓存为空时从Nacos拉取)
│ 应用HealthCheck过滤掉不健康实例
│ 应用ZonePreference过滤跨区域实例
▼
ReactorServiceInstanceLoadBalancer.choose(request)
│ 应用负载均衡算法(轮询/随机/自定义)
▼
返回选定的 ServiceInstance
│ ServiceInstance 包含 ip、port、metadata 等信息
▼
reconstructURI() 将服务名替换为 IP:port
│ http://service-user/api/user → http://192.168.1.100:8081/api/user
▼
发起真实HTTP请求
关键理解 :ServiceInstanceListSupplier和ReactorServiceInstanceLoadBalancer的分工是------前者负责"有哪些实例可选"(过滤后的候选列表),后者负责"从候选中选哪个"(算法决策)。
3.3 核心接口源码解析
ReactorServiceInstanceLoadBalancer接口------负载均衡算法的实现入口:
java
public interface ReactorServiceInstanceLoadBalancer extends ReactorLoadBalancer<ServiceInstance> {
Mono<Response<ServiceInstance>> choose(Request request);
}
RoundRobinLoadBalancer源码核心逻辑:
java
public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final AtomicInteger position; // 原子计数器
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
private Response<ServiceInstance> processInstanceResponse(
ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
Response<ServiceInstance> response = getInstanceResponse(serviceInstances);
if (supplier instanceof SelectedInstanceCallback && response.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance(response.getServer());
}
return response;
}
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
return new EmptyResponse();
}
int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
ServiceInstance instance = instances.get(pos % instances.size());
return new DefaultResponse(instance);
}
}
核心逻辑解读:
position.incrementAndGet() & Integer.MAX_VALUE:原子递增计数器,& Integer.MAX_VALUE确保结果始终为正数(防止溢出为负数)pos % instances.size():取模运算,确保索引在实例列表范围内循环- 每次调用递增计数器,实现"轮流选择"的效果
ServiceInstanceListSupplier接口------实例列表的供应入口:
java
public interface ServiceInstanceListSupplier extends Supplier<Flux<List<ServiceInstance>>> {
String getServiceId();
default Flux<List<ServiceInstance>> get() {
return get(null);
}
default Flux<List<ServiceInstance>> get(Request request) {
return get();
}
}
SCLB提供了多种ServiceInstanceListSupplier实现,通过Builder模式组合:
| Supplier类型 | 功能 | 是否必需 |
|---|---|---|
DiscoveryClientServiceInstanceListSupplier |
从DiscoveryClient获取实例列表 | ✅ 必需 |
CachingServiceInstanceListSupplier |
缓存实例列表(默认TTL 35秒) | 推荐 |
HealthCheckServiceInstanceListSupplier |
主动健康检查,过滤不健康实例 | 推荐 |
ZonePreferenceServiceInstanceListSupplier |
区域感知,优先选择同区域实例 | 按需 |
Builder模式的典型用法:
java
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withCaching()
.withHealthChecks()
.build(context);
}
}
生产建议 :官方文档明确指出,虽然基础的非缓存实现 对原型设计和测试有用,但效率远低于缓存版本,因此生产环境推荐始终使用缓存版本。
四、负载均衡算法深度剖析
4.1 默认策略:轮询(RoundRobin)
SCLB的默认策略是轮询 ,按照实例列表的顺序依次选择,到达末尾后重新开始。轮询策略适用于所有实例性能相近的场景,保证每个实例获得均等的流量分配。
算法特点:
- 实现简单,无状态依赖
- 天然公平,每个实例获得等量请求
- 不感知实例的负载情况
源码实现 :如第3.3节所示,通过AtomicInteger计数器实现。
4.2 随机策略(Random)
随机策略从可用实例列表中完全随机 选择一个实例。适用于实例性能相近且需要避免热点集中的场景。
为什么默认策略不是随机? 轮询比随机更可预测,在实例数量少时可以保证严格均衡。随机的优势在于当实例数量多时,随机分布更均匀,且避免了轮询在实例数量变化时可能出现的"突变"。
配置方式:
java
public class CustomLoadBalancerConfiguration {
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
4.3 加权策略(Weighted)
当服务实例的性能不一致时(如不同规格的机器),轮询和随机策略都无法实现按性能比例分配流量。SCLB提供了加权负载均衡支持。
Nacos权重集成 :在Spring Cloud Alibaba环境中,Nacos实例的weight字段是元数据属性。但Nacos本身不强制流量分配------客户端框架读取权重并在负载均衡时应用。
关键问题 :默认情况下,Spring Cloud Alibaba将Nacos权重视为二值(零权重表示不接收流量,非零权重表示均等流量)。要启用真正的加权负载均衡,需要激活Nacos感知的负载均衡器:
yaml
spring:
cloud:
loadbalancer:
nacos:
enabled: true
或者直接启用SCLB的加权Supplier:
yaml
spring:
cloud:
loadbalancer:
configurations: weighted
也可以通过编程方式配置:
java
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withCaching()
.withWeighted()
.build(context);
}
}
4.4 区域感知策略(Zone Preference)
区域感知是分布式部署中减少跨区域网络延迟的关键能力。当服务实例分布在多个可用区(Zone)或数据中心时,客户端应优先选择同区域的实例,仅在本地区域无可用实例时才跨区域调用。
配置方式:
yaml
spring:
cloud:
loadbalancer:
configurations: zone-preference
或编程方式:
java
public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withCaching()
.withZonePreference()
.build(context);
}
}
区域信息传递 :区域信息通过实例元数据中的zone字段传递。在Nacos中,可以通过实例元数据设置:
yaml
spring:
cloud:
nacos:
discovery:
metadata:
zone: cn-shenzhen-a
4.5 同实例优先策略(Same Instance Preference)
该策略会优先选择上一次选中的实例,如果该实例仍然可用。适用于需要保持会话粘性的场景。这也是Zookeeper StickyRule的替代方案。
配置方式:
yaml
spring:
cloud:
loadbalancer:
configurations: same-instance-preference
4.6 策略组合与优先级
多个Supplier可以叠加使用,执行顺序由Builder的调用顺序决定:
java
ServiceInstanceListSupplier.builder()
.withDiscoveryClient() // 1. 从注册中心获取实例
.withHealthChecks() // 2. 过滤不健康实例
.withZonePreference() // 3. 区域优先过滤
.withCaching() // 4. 缓存最终结果
.build(context);
执行顺序即为过滤链的顺序 。withHealthChecks()放在withZonePreference()之前,意味着先过滤不健康实例,再做区域优先选择。
五、Nacos集成实战
5.1 依赖配置
在服务消费者模块(如service-order)的POM中添加:
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
注意 :在Spring Cloud 2025.1.x中,如果使用了
spring-cloud-starter-alibaba-nacos-discovery,LoadBalancer依赖会自动传递引入。但显式声明仍然推荐,以确保版本可控。
5.2 启用LoadBalancer
在配置类中添加@LoadBalanced注解:
java
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@LoadBalanced注解给RestTemplate添加了一个拦截器LoadBalancerInterceptor,当请求URL中的主机名是服务名时,拦截器会调用LoadBalancer选择实例,然后将服务名替换为实际的IP:端口。
5.3 验证负载均衡
启动两个service-user实例(8081和8083),通过service-order多次调用用户接口,观察两个实例的请求分布。默认的轮询策略会交替选择两个实例。
5.4 Nacos感知的权重负载均衡
如果需要Nacos权重生效,在service-order的application.yml中添加:
yaml
spring:
cloud:
loadbalancer:
nacos:
enabled: true
configurations: weighted
然后在Nacos控制台修改实例权重,观察流量按权重分配。
六、2025版新特性
Spring Cloud 2025.1.0(Oakwood)为LoadBalancer带来了多项增强:
特性一:LoadBalancer API版本控制支持。Spring Cloud Commons添加了LoadBalancer API的版本控制支持,可以根据请求头或参数中的API版本信息,选择对应版本的实例。这为灰度发布和API版本管理提供了底层能力。
特性二:JSpecify空安全注解。所有公共API类使用JSpecify进行空安全性注解,IDE可以更准确地提示潜在的NullPointerException,减少运行时崩溃。
特性三:Spring Interface Clients集成 。Spring Interface Clients AutoConfiguration集成了负载均衡器,通过@HttpExchange声明式客户端可以直接使用LoadBalancer进行服务调用。
七、生产环境踩坑指南
坑一:滚动发布期间调用失败------缓存是"真凶"
现象 :滚动发布时,大量调用失败,报Connection refused或Timeout。
原因 :SCLB默认的CachingServiceInstanceListSupplier缓存TTL为35秒。在这35秒内,即使Nacos已经推送了实例变更,SCLB仍然使用缓存的旧实例列表,导致请求被路由到已下线的实例。
解决方案:在Nacos 2.x/3.x环境中,关闭SCLB缓存或缩短缓存TTL:
yaml
spring:
cloud:
loadbalancer:
cache:
enabled: false # 关闭缓存
或调整缓存TTL:
yaml
spring:
cloud:
loadbalancer:
cache:
ttl: 5s # 缩短至5秒
更优方案 :自定义ServiceInstanceListSupplier,直接监听Nacos的实例变更事件,实现实时刷新。
坑二:Nacos权重修改后不生效
现象:在Nacos控制台修改权重后,流量分布没有变化。
原因 :Spring Cloud Alibaba默认将Nacos权重视为二值(零权重不接收流量,非零权重均等流量),不启用真正的加权路由。
解决 :配置spring.cloud.loadbalancer.nacos.enabled=true和spring.cloud.loadbalancer.configurations=weighted。
坑三:LoadBalancer与Ribbon同时存在classpath
现象:启动时报类冲突或负载均衡行为异常。
原因:项目中同时存在Ribbon和LoadBalancer的依赖。
解决:排除Ribbon依赖,确保classpath中只有LoadBalancer。
坑四:区域感知配置后仍然跨区域调用
现象 :配置了zone-preference后,请求仍然路由到其他区域的实例。
原因 :实例元数据中未设置zone字段,或所有实例在同一个Zone中。
解决 :在Nacos实例元数据中设置zone字段,并确保实例分布在不同区域。
坑五:自定义策略不生效
现象 :自定义的ReactorServiceInstanceLoadBalancer Bean未生效。
原因 :自定义配置类被@ComponentScan扫描到,导致全局生效(影响了所有服务)。
解决 :自定义配置类不应放在@ComponentScan扫描路径下,应通过@LoadBalancerClient或@LoadBalancerClients注解指定为特定服务生效。
八、课后作业
作业一 :在service-order中确认已引入spring-cloud-starter-loadbalancer依赖,启动两个service-user实例(8081和8083),通过@LoadBalanced RestTemplate多次调用,观察轮询效果。
作业二 :将service-order的负载均衡策略切换为随机策略,配置自定义RandomLoadBalancer,验证两个实例的请求分布变为随机。
作业三 :配置spring.cloud.loadbalancer.nacos.enabled=true,在Nacos控制台将8083的权重设为10,验证流量按权重分配(约91%流量到8081)。
作业四(进阶) :实现一个自定义的ReactorServiceInstanceLoadBalancer,基于实例的metadata.version字段实现版本感知的负载均衡(优先选择指定版本的实例)。通过@LoadBalancerClient注解将其应用于service-user。
九、下节预告
第14课将进入OpenFeign声明式远程调用完整实战。我们将从Feign核心原理讲到接口式调用的完整实战,涵盖参数传递、文件上传下载、请求头传递、超时配置等高频使用场景。LoadBalancer作为底层负载均衡引擎,将在OpenFeign的调用链中自动生效。理解本课的LoadBalancer原理后,第14课的Feign调用将更加"知其所以然"------为什么Feign能通过服务名调用、为什么默认是轮询策略、如何自定义负载均衡行为,这些问题都将在第14课中得到实战验证。
🔗《最新版 SpringCloud 2025 从入门到实战》系列课程导航
第一部分:微服务前置基础 & 新版环境搭建(第1-5课)
第二部分:注册中心核心(Nacos 最新版)(第6-9课)
第三部分:配置中心核心(Nacos配置中心)(第10-12课)
第四部分:服务通信核心(OpenFeign + LoadBalancer)(第13-16课)
第五部分:网关核心(SpringCloud Gateway 新版)(第17-20课)
第六部分:熔断、限流、降级(Sentinel 新版)(第21-24课)
第七部分:微服务监控、链路追踪、日志体系(第25-28课)
第八部分:微服务高阶特性 & 分布式核心能力(第29-31课)
第九部分:企业级完整项目实战 & 架构复盘(第32-35课)