Ribbon 曾经是 Spring Cloud 家族默认的客户端负载均衡工具,而 Spring Cloud LoadBalancer (SCLB) 是官方替换 Ribbon 的新实现。表面上它们都解决 “服务调用时选哪个实例” 的问题,但在理念、架构和生态上差异不小。
一、Ribbon vs SCLB
1. 定位和生态地位
Ribbon
Netflix OSS 出品,老一代的客户端负载均衡器。
在 Spring Cloud Dalston ~ Greenwich 时代是默认选择。
后来 Netflix OSS 宣布 进入维护模式(2018年起不再活跃发展)。
Spring Cloud LoadBalancer
Spring 团队自研,替代 Ribbon。
完全独立于 Netflix 生态,不再依赖过时组件。
与 Spring Boot 2.x/3.x、Reactor、WebClient、Feign 等深度集成。
2. 核心设计思路
Ribbon
侵入性较强:依赖
IClientConfig
、IRule
、IPing
等接口,配置体系复杂。强调“策略类 + 配置类”模式(比如 RoundRobinRule、ZoneAvoidanceRule)。
同步调用模型为主,虽然可扩展但偏“重量级”。
与
RestTemplate
深度耦合(通过@LoadBalanced RestTemplate
)。
Spring Cloud LoadBalancer
函数式 + 轻量化:核心是
ServiceInstanceListSupplier
(负责提供实例列表)和ReactorServiceInstanceLoadBalancer
(负责挑选实例)。完全 Reactor 化,支持响应式编程(Reactor/Flux/Mono),天然适配 WebClient。
API 更简单,默认策略是 RoundRobin,但很容易定制。
更解耦,和 DiscoveryClient、Feign、gRPC 等可自由组合。
3. 扩展能力
Ribbon
有比较多的现成策略:
RoundRobinRule
、RandomRule
、RetryRule
、WeightedResponseTimeRule
…自定义需要继承
IRule
,配置也要绕 Ribbon 的专用配置体系。支持 Zone 概念(跨机房/多可用区),适合 Netflix 内部环境,但在普通企业里很少用上。
Spring Cloud LoadBalancer
策略很“干净”:只要实现
ReactorServiceInstanceLoadBalancer
接口即可。ServiceInstanceListSupplier 提供了天然的 hook:你可以在实例列表进入负载均衡前加上 过滤、排序、权重。
没有 Ribbon 那种内置十几个策略的复杂度,但用组合的方式,灵活度更高。
4. 与 Spring 生态的关系
Ribbon
被强绑定到
RestTemplate
+ Feign(老版本)。Spring Cloud Netflix 维护成本高,升级阻力大。
Spring Cloud LoadBalancer
未来路线核心组件,和 Spring Cloud Gateway、Feign 3.x、WebClient 全兼容。
官方推荐替代方案,Ribbon 已经标记 Deprecated。
随 Spring Boot/Spring Cloud 版本更新,能持续获得支持。
5. 性能与现代化
Ribbon
基于老旧同步模型(虽然功能全,但显得笨重)。
没有天然的 Reactor 支持,在响应式场景里不合拍。
Spring Cloud LoadBalancer
基于 Reactor,轻量、非阻塞,天然适合高并发场景。
使用
Flux<ServiceInstance>
,可以灵活叠加缓存、权重、健康检查逻辑。更加云原生,能和 Kubernetes Service、Consul、Nacos 等平滑对接。
二、源码说明
1. 核心抽象
Ribbon
结果:实例获取、缓存、负载均衡策略,全都绑死在 Ribbon 的体系。
负载均衡器接口:
ILoadBalancer
内部维护实例列表(ServerList),并交给IRule
挑选。负载均衡策略接口:
IRule
public interface IRule { Server choose(Object key); void setLoadBalancer(ILoadBalancer lb); ILoadBalancer getLoadBalancer(); }
代表“从一堆服务实例里挑一个”。
Spring Cloud LoadBalancer (SCLB)
实例供应接口:
public interface ServiceInstanceListSupplier extends Supplier<Flux<List<ServiceInstance>>> { String getServiceId(); }
负责“提供候选实例列表”,来源可以是 DiscoveryClient、缓存、静态配置。
策略接口:
public interface ReactorServiceInstanceLoadBalancer { Mono<Response<ServiceInstance>> choose(Request request); }
专注于“如何从候选列表里挑一个”。
结果:候选列表与选择逻辑完全解耦,职责单一,而且基于 Reactor(非阻塞)。
2. Spring Cloud LoadBalancer 和 Feign 相对 Ribbon 的解耦性
看源码上的调用链对比最明显:
Ribbon + Feign
Feign 的LoadBalancerFeignClient
→ 直接调用RibbonLoadBalancerClient
→ 使用ILoadBalancer
+IRule
绑定。public class RibbonLoadBalancerClient implements LoadBalancerClient { @Override public ServiceInstance choose(String serviceId) { ILoadBalancer loadBalancer = getLoadBalancer(serviceId); Server server = loadBalancer.chooseServer(null); return new RibbonServer(serviceId, server, isSecure(server), serverIntrospector(serviceId).getMetadata(server)); } }
可以看到:Feign 和 Ribbon 耦合紧密,
ILoadBalancer
和IRule
必须都存在。SCLB + Feign(Spring Cloud 2020+)
Feign 的LoadBalancerFeignClient
→ 使用BlockingLoadBalancerClient
或ReactorLoadBalancerExchangeFilterFunction
。public class BlockingLoadBalancerClient implements LoadBalancerClient { private final LoadBalancerClientFactory clientFactory; @Override public ServiceInstance choose(String serviceId) { ReactorServiceInstanceLoadBalancer loadBalancer = clientFactory.getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class); // 核心是调用 loadBalancer.choose() } }
这里的关键:
Feign 只依赖于
ReactorServiceInstanceLoadBalancer
抽象,而不关心实例供应如何实现。ServiceInstanceListSupplier
可随时替换(比如 Kubernetes、Consul、Nacos),Feign 本身无需改动。
👉 结论:Ribbon 时代 Feign 直接依赖 Ribbon 核心接口,导致强绑定;SCLB 下 Feign 只依赖于统一的 LoadBalancer 抽象,而实例来源和策略完全可插拔 → 解耦性更高。
三、Spring Cloud LoadBalancer 最佳实践
1. 基础使用
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
RestTemplate / WebClient 集成
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
建议优先用 WebClient,因为它能发挥 SCLB 的 Reactor 非阻塞优势。
2. 缓存与性能优化
SCLB 默认每次请求会调用 DiscoveryClient
获取实例,可以用内置的 缓存供应器:
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withCaching() // 启用缓存
.build(context);
}
这样能减少注册中心压力,尤其是在高并发场景。
3. 自定义策略
简单示例:基于元数据的优先级选择
比如实例 metadata 里有 "zone": "shanghai"
,只要优先调用同城实例:
@Bean
ReactorServiceInstanceLoadBalancer zoneAwareLoadBalancer(
ObjectProvider<ServiceInstanceListSupplier> supplierProvider) {
return new ReactorServiceInstanceLoadBalancer() {
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
return supplierProvider.getIfAvailable().get()
.next()
.map(instances -> {
List<ServiceInstance> localZone = instances.stream()
.filter(i -> "shanghai".equals(i.getMetadata().get("zone")))
.toList();
if (!localZone.isEmpty()) {
return new DefaultResponse(localZone.get(0));
}
return new DefaultResponse(instances.get(0));
});
}
};
}
更复杂:权重路由
实现 ReactorServiceInstanceLoadBalancer
,结合 metadata.weight
字段,做加权随机选择。这相当于 Ribbon 的 WeightedResponseTimeRule
,但在 SCLB 里更灵活。
4. ServiceInstanceListSupplier 增强
SCLB 提供了“供应链”思想,常用扩展点:
过滤:过滤掉 metadata 标记为
"status=down"
的实例;排序:按响应时间/CPU负载排序,把健康的放前面;
包装:组合多层供应器(比如先 DiscoveryClient → 再缓存 → 再 zone 过滤)。
这种链式增强比 Ribbon 的配置类清晰得多。
5. 与 Feign 配合
在新版 Spring Cloud 中,Feign 默认走 SCLB。最佳实践:
配置
spring.cloud.loadbalancer.retry.enabled=true
→ 自动开启重试机制;自定义
ReactorServiceInstanceLoadBalancer
,Feign 会自动走你的策略;如果想做更细粒度控制,可以写
@FeignClient(configuration=...)
指定独立的 LoadBalancer 配置。
6. 容错与重试
SCLB 不再像 Ribbon 那样内置 RetryRule,而是把重试交给 Spring Retry:
spring:
cloud:
loadbalancer:
retry:
enabled: true
retry-on-all-operations: true
max-retries-on-same-service-instance: 1
max-retries-on-next-service-instance: 2
这样能保证单实例失败 → 自动切换下一个实例,避免单点问题。
7. 云原生环境最佳实践
在 Kubernetes 上,推荐直接基于
spring-cloud-starter-kubernetes-client
+ SCLB,实例供应就是 Pod 列表;在 Nacos / Consul 场景下,直接使用对应 DiscoveryClient starter,SCLB 自动适配;
保持 ServiceInstance metadata 丰富(zone、weight、tag),方便做策略。
8. 迁移建议(Ribbon → SCLB)
RestTemplate 上的
@LoadBalanced
不需要动,底层自动切换为 SCLB;Feign 默认走 SCLB,不需要额外改动;
Ribbon 自定义的
IRule
策略,需要改写成ReactorServiceInstanceLoadBalancer
;Ribbon 的 Zone/Weight 逻辑,迁移到
ServiceInstanceListSupplier
+ metadata 策略更清晰。
总结
最佳实践关键就是 “解耦 + 插拔”:
实例来源 → DiscoveryClient + 缓存
实例增强 → Supplier 过滤/排序/权重
策略 → 自定义 LoadBalancer
调用 → WebClient / Feign
容错 → Spring Retry