文章目录
- 一、开篇:Ribbon的“退场”与新王的“登基”
- 二、Ribbon淘汰原因深度剖析
- 2.1 三大退役原因
- 2.2 替代关系对照
- 三、LoadBalancer核心架构解析
- 3.1 核心组件架构
- 3.2 核心流程详解
- 3.3 核心接口源码解析
- 四、负载均衡算法深度剖析
- 4.1 默认策略:轮询(RoundRobin)
- 4.2 随机策略(Random)
- 4.3 加权策略(Weighted)
- 4.4 区域感知策略(Zone Preference)
- 4.5 同实例优先策略(Same Instance Preference)
- 4.6 策略组合与优先级
- 五、Nacos集成实战
- 5.1 依赖配置
- 5.2 启用LoadBalancer
- 5.3 验证负载均衡
- 5.4 Nacos感知的权重负载均衡
- 六、2025版新特性
- 七、生产环境踩坑指南
- 坑一:滚动发布期间调用失败——缓存是“真凶”
- 坑二:Nacos权重修改后不生效
- 坑三:LoadBalancer与Ribbon同时存在classpath
- 坑四:区域感知配置后仍然跨区域调用
- 坑五:自定义策略不生效
- 八、课后作业
- 九、下节预告
- 🔗《最新版 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接口——负载均衡算法的实现入口:
publicinterfaceReactorServiceInstanceLoadBalancerextendsReactorLoadBalancer<ServiceInstance>{Mono<Response<ServiceInstance>>choose(Requestrequest);}RoundRobinLoadBalancer源码核心逻辑:
publicclassRoundRobinLoadBalancerimplementsReactorServiceInstanceLoadBalancer{privatefinalAtomicIntegerposition;// 原子计数器@OverridepublicMono<Response<ServiceInstance>>choose(Requestrequest){ServiceInstanceListSuppliersupplier=serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);returnsupplier.get(request).next().map(serviceInstances->processInstanceResponse(supplier,serviceInstances));}privateResponse<ServiceInstance>processInstanceResponse(ServiceInstanceListSuppliersupplier,List<ServiceInstance>serviceInstances){Response<ServiceInstance>response=getInstanceResponse(serviceInstances);if(supplierinstanceofSelectedInstanceCallback&&response.hasServer()){((SelectedInstanceCallback)supplier).selectedServiceInstance(response.getServer());}returnresponse;}privateResponse<ServiceInstance>getInstanceResponse(List<ServiceInstance>instances){if(instances.isEmpty()){returnnewEmptyResponse();}intpos=this.position.incrementAndGet()&Integer.MAX_VALUE;ServiceInstanceinstance=instances.get(pos%instances.size());returnnewDefaultResponse(instance);}}核心逻辑解读:
position.incrementAndGet() & Integer.MAX_VALUE:原子递增计数器,& Integer.MAX_VALUE确保结果始终为正数(防止溢出为负数)pos % instances.size():取模运算,确保索引在实例列表范围内循环- 每次调用递增计数器,实现“轮流选择”的效果
ServiceInstanceListSupplier接口——实例列表的供应入口:
publicinterfaceServiceInstanceListSupplierextendsSupplier<Flux<List<ServiceInstance>>>{StringgetServiceId();defaultFlux<List<ServiceInstance>>get(){returnget(null);}defaultFlux<List<ServiceInstance>>get(Requestrequest){returnget();}}SCLB提供了多种ServiceInstanceListSupplier实现,通过Builder模式组合:
| Supplier类型 | 功能 | 是否必需 |
|---|---|---|
DiscoveryClientServiceInstanceListSupplier | 从DiscoveryClient获取实例列表 | ✅ 必需 |
CachingServiceInstanceListSupplier | 缓存实例列表(默认TTL 35秒) | 推荐 |
HealthCheckServiceInstanceListSupplier | 主动健康检查,过滤不健康实例 | 推荐 |
ZonePreferenceServiceInstanceListSupplier | 区域感知,优先选择同区域实例 | 按需 |
Builder模式的典型用法:
publicclassCustomLoadBalancerConfiguration{@BeanpublicServiceInstanceListSupplierdiscoveryClientServiceInstanceListSupplier(ConfigurableApplicationContextcontext){returnServiceInstanceListSupplier.builder().withDiscoveryClient().withCaching().withHealthChecks().build(context);}}生产建议:官方文档明确指出,虽然基础的非缓存实现对原型设计和测试有用,但效率远低于缓存版本,因此生产环境推荐始终使用缓存版本。
四、负载均衡算法深度剖析
4.1 默认策略:轮询(RoundRobin)
SCLB的默认策略是轮询,按照实例列表的顺序依次选择,到达末尾后重新开始。轮询策略适用于所有实例性能相近的场景,保证每个实例获得均等的流量分配。
算法特点:
- 实现简单,无状态依赖
- 天然公平,每个实例获得等量请求
- 不感知实例的负载情况
源码实现:如第3.3节所示,通过AtomicInteger计数器实现。
4.2 随机策略(Random)
随机策略从可用实例列表中完全随机选择一个实例。适用于实例性能相近且需要避免热点集中的场景。
为什么默认策略不是随机?轮询比随机更可预测,在实例数量少时可以保证严格均衡。随机的优势在于当实例数量多时,随机分布更均匀,且避免了轮询在实例数量变化时可能出现的“突变”。
配置方式:
publicclassCustomLoadBalancerConfiguration{@BeanpublicReactorLoadBalancer<ServiceInstance>randomLoadBalancer(Environmentenvironment,LoadBalancerClientFactoryloadBalancerClientFactory){Stringname=environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);returnnewRandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class),name);}}4.3 加权策略(Weighted)
当服务实例的性能不一致时(如不同规格的机器),轮询和随机策略都无法实现按性能比例分配流量。SCLB提供了加权负载均衡支持。
Nacos权重集成:在Spring Cloud Alibaba环境中,Nacos实例的weight字段是元数据属性。但Nacos本身不强制流量分配——客户端框架读取权重并在负载均衡时应用。
关键问题:默认情况下,Spring Cloud Alibaba将Nacos权重视为二值(零权重表示不接收流量,非零权重表示均等流量)。要启用真正的加权负载均衡,需要激活Nacos感知的负载均衡器:
spring:cloud:loadbalancer:nacos:enabled:true或者直接启用SCLB的加权Supplier:
spring:cloud:loadbalancer:configurations:weighted也可以通过编程方式配置:
publicclassCustomLoadBalancerConfiguration{@BeanpublicServiceInstanceListSupplierdiscoveryClientServiceInstanceListSupplier(ConfigurableApplicationContextcontext){returnServiceInstanceListSupplier.builder().withDiscoveryClient().withCaching().withWeighted().build(context);}}4.4 区域感知策略(Zone Preference)
区域感知是分布式部署中减少跨区域网络延迟的关键能力。当服务实例分布在多个可用区(Zone)或数据中心时,客户端应优先选择同区域的实例,仅在本地区域无可用实例时才跨区域调用。
配置方式:
spring:cloud:loadbalancer:configurations:zone-preference或编程方式:
publicclassCustomLoadBalancerConfiguration{@BeanpublicServiceInstanceListSupplierdiscoveryClientServiceInstanceListSupplier(ConfigurableApplicationContextcontext){returnServiceInstanceListSupplier.builder().withDiscoveryClient().withCaching().withZonePreference().build(context);}}区域信息传递:区域信息通过实例元数据中的zone字段传递。在Nacos中,可以通过实例元数据设置:
spring:cloud:nacos:discovery:metadata:zone:cn-shenzhen-a4.5 同实例优先策略(Same Instance Preference)
该策略会优先选择上一次选中的实例,如果该实例仍然可用。适用于需要保持会话粘性的场景。这也是Zookeeper StickyRule的替代方案。
配置方式:
spring:cloud:loadbalancer:configurations:same-instance-preference4.6 策略组合与优先级
多个Supplier可以叠加使用,执行顺序由Builder的调用顺序决定:
ServiceInstanceListSupplier.builder().withDiscoveryClient()// 1. 从注册中心获取实例.withHealthChecks()// 2. 过滤不健康实例.withZonePreference()// 3. 区域优先过滤.withCaching()// 4. 缓存最终结果.build(context);执行顺序即为过滤链的顺序。withHealthChecks()放在withZonePreference()之前,意味着先过滤不健康实例,再做区域优先选择。
五、Nacos集成实战
5.1 依赖配置
在服务消费者模块(如service-order)的POM中添加:
<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注解:
@ConfigurationpublicclassRestTemplateConfig{@Bean@LoadBalancedpublicRestTemplaterestTemplate(){returnnewRestTemplate();}}@LoadBalanced注解给RestTemplate添加了一个拦截器LoadBalancerInterceptor,当请求URL中的主机名是服务名时,拦截器会调用LoadBalancer选择实例,然后将服务名替换为实际的IP:端口。
5.3 验证负载均衡
启动两个service-user实例(8081和8083),通过service-order多次调用用户接口,观察两个实例的请求分布。默认的轮询策略会交替选择两个实例。
5.4 Nacos感知的权重负载均衡
如果需要Nacos权重生效,在service-order的application.yml中添加:
spring:cloud:loadbalancer:nacos:enabled:trueconfigurations: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:
spring:cloud:loadbalancer:cache:enabled:false# 关闭缓存或调整缓存TTL:
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字段,并确保实例分布在不同区域。
坑五:自定义策略不生效
现象:自定义的ReactorServiceInstanceLoadBalancerBean未生效。
原因:自定义配置类被@ComponentScan扫描到,导致全局生效(影响了所有服务)。
解决:自定义配置类不应放在@ComponentScan扫描路径下,应通过@LoadBalancerClient或@LoadBalancerClients注解指定为特定服务生效。
八、课后作业
作业一:在service-order中确认已引入spring-cloud-starter-loadbalancer依赖,启动两个service-user实例(8081和8083),通过@LoadBalancedRestTemplate多次调用,观察轮询效果。
作业二:将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课)