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