在 Spring Cloud LoadBalancer 中,ServiceInstanceListSupplier 是负责提供服务实例列表的核心组件,是负载均衡流程中“获取可用实例”的关键环节。它直接对接服务发现组件(如 Eureka、Nacos 等),并可对实例列表进行过滤、转换或缓存,为负载均衡策略(如轮询、随机)提供决策所需的实例数据。
ServiceInstanceListSupplier 作为“服务实例数据源”,封装了从服务注册中心获取实例列表的逻辑,并支持对实例进行预处理(如健康检查过滤、元数据转换)。
在实际开发中,通过配置或自定义 ServiceInstanceListSupplier,可实现对实例列表的精细化控制,满足灰度发布、区域路由等复杂业务需求。
ServiceInstanceListSupplier 有多个内置实现类,形成了“装饰器模式”的层级结构,通过组合实现复杂的实例处理逻辑。核心实现如下:

其中:
DiscoveryClientServiceInstanceListSupplier 基础实现,对接 DiscoveryClient(服务发现客户端),从注册中心获取原始实例列表。
NoopServiceInstanceListSupplier 空实现,用于测试或占位场景,不提供实际的服务实例列表。
DelegatingServiceInstanceListSupplier 🌈委托型实现,作为装饰器的基类,用于组合多个 ServiceInstanceListSupplier 的功能,实现职责链模式。
WeightedServiceInstanceListSupplier 支持加权的 ServiceInstanceListSupplier 实现,可根据实例的权重属性进行实例筛选或排序,适配加权负载均衡场景。
HintBasedServiceInstanceListSupplier 基于提示(Hint)的实例筛选,可根据请求上下文中的提示信息(如特定标签、版本)过滤实例,适用于灰度发布等场景。
HealthCheckServiceInstanceListSupplier 带健康检查的 ServiceInstanceListSupplier 实现,过滤掉不健康的服务实例,确保仅返回健康的实例供负载均衡选择。
RequestBasedStickySessionServiceInstanceListSupplier 基于请求的粘性会话 ServiceInstanceListSupplier,确保同一请求(或用户)的多次调用路由到同一实例,适用于需要会话保持的场景。
SameInstancePreferenceServiceInstanceListSupplier 优先选择上次使用的实例,减少实例切换带来的开销,提升请求连续性。
CachingServiceInstanceListSupplier 带缓存的 ServiceInstanceListSupplier 实现,对实例列表进行缓存以减少对服务注册中心的频繁查询,提升性能。
RetryAwareServiceInstanceListSupplier 支持重试的 ServiceInstanceListSupplier 实现,在获取实例失败时可触发重试逻辑,增强可靠性。
SubsetServiceInstanceListSupplier 子集 ServiceInstanceListSupplier 实现,从全量实例中选择一个子集进行负载均衡,适用于大集群下的实例分组场景。
ZonePreferenceServiceInstanceListSupplier 基于区域偏好的 ServiceInstanceListSupplier 实现,优先选择与调用方同区域的实例,降低跨区域调用的网络延迟。
ServiceInstanceListSupplier 接口定义如下:
package org.springframework.cloud.loadbalancer.core;
import java.util.List;
import java.util.function.Supplier;
import reactor.core.publisher.Flux;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.Request;
/**
* 服务实例列表供应商接口,负责提供服务实例的反应式数据流,是负载均衡中"实例数据源"的核心抽象。
* 作为连接服务发现组件(如Eureka、Nacos)与负载均衡策略的桥梁,它封装了服务实例的获取、
* 过滤、动态更新等逻辑,为负载均衡器提供实时可用的实例列表。
*
* 该接口继承自 Supplier,但返回值为响应式流 Flux,支持实例列表的动态推送
* (如服务上下线、健康状态变更时自动更新),适配云原生环境下服务实例的动态变化特性。
*
* @author Olga Maciaszek-Sharma
* @since 2.2.0
* @see DiscoveryClientServiceInstanceListSupplier 基于服务发现客户端的默认实现
* @see CachingServiceInstanceListSupplier 带缓存功能的装饰器实现
* @see HealthCheckServiceInstanceListSupplier 带健康检查过滤的装饰器实现
*/
public interface ServiceInstanceListSupplier extends Supplier<Flux<List<ServiceInstance>>> {
/**
* 获取当前供应商对应的服务唯一标识(serviceId)。
* 每个服务实例列表供应商绑定一个具体的服务,此方法用于标识其对应的服务ID,
* 确保负载均衡器能正确关联到目标服务的实例列表。
*
* @return 服务唯一标识(如"user-service")
*/
String getServiceId();
/**
* 基于请求上下文获取服务实例列表的响应式流。
* 默认实现直接调用 get(),即不依赖请求上下文;
* 子类可重写此方法,实现基于请求特征(如请求参数、Header)的动态实例筛选
* (如灰度发布中根据请求的版本信息过滤实例)。
*
* @param request 包含请求上下文的对象,可携带额外信息用于实例筛选
* @return 服务实例列表的响应式流(Flux),当实例发生变化时会推送最新列表
*/
default Flux<List<ServiceInstance>> get(Request request) {
return get();
}
/**
* 创建一个 ServiceInstanceListSupplierBuilder 构建器,用于便捷地构建
* 带装饰器的 ServiceInstanceListSupplier 实例(如组合缓存、健康检查等功能)。
*
* @return 服务实例列表供应商的构建器
*/
static ServiceInstanceListSupplierBuilder builder() {
return new ServiceInstanceListSupplierBuilder();
}
/**
* 从服务发现源获取服务实例列表的响应式流(继承自 Supplier 的核心方法)。
* 返回的 Flux 会持续推送最新的实例列表:
* - 初始订阅时推送当前可用实例
* - 当服务实例发生变化(如上下线、健康状态变更)时,自动推送更新后的列表
* 该流通常会经过多层装饰器处理(如过滤不健康实例、缓存结果)后提供给负载均衡器。
*
* @return 服务实例列表的反应式流,非空(若无可可用实例则返回空列表)
*/
@Override
Flux<List<ServiceInstance>> get();
}从源码可知,总共提供了三个方法,通过 get() 或 get(Request request) 从注册中心获取服务实例列表。
注意:如果不了解装饰模式,可以先熟悉一下,便于后续阅读。
在“关键实现类与层级关系”部分简单介绍了 DelegatingServiceInstanceListSupplier 类,它是装饰器模式的核心实现类,属于 ServiceInstanceListSupplier 家族中的“委托型” ServiceInstanceListSupplier。它通过包装其他 ServiceInstanceListSupplier 实例,实现对实例列表的多层处理(如过滤、缓存、健康检查等),是构建复杂实例处理逻辑的基础组件。
ServiceInstanceListSupplier 通过委托机制实现了实例列表的“多层处理”能力。它让健康检查、缓存、区域过滤等功能可独立实现并灵活组合,是构建复杂负载均衡逻辑的关键组件。在实际使用中,开发者通常无需直接操作该类,而是通过其众多子类(如 HealthCheck、Caching 等)来定制实例处理流程。
DelegatingServiceInstanceListSupplier 在装饰模式中的位置如下图:

简单分析:
ServiceInstanceListSupplier 作为 Component 接口:定义了获取服务实例列表的统一契约,是整个模式的核心抽象。
DiscoveryClientServiceInstanceListSupplier 作为 ConcreteComponent:是 “被装饰的基础组件”,负责从服务发现客户端(如 Eureka、Nacos)获取原始实例列表,提供最基础的实例数据源。
DelegatingServiceInstanceListSupplier 作为 Decorator 抽象装饰器:持有 ServiceInstanceListSupplier 类型的 delegate 引用,通过委托机制实现对其他 ServiceInstanceListSupplier 的功能扩展,是所有装饰器子类的基类。
WeightedServiceInstanceListSupplier 和 HealthCheckServiceInstanceListSupplier 作为 ConcreteDecorator:是具体的装饰器实现,分别在 delegate 的基础上添加“加权筛选”和“健康检查过滤”的额外行为,实现了功能的动态扩展。
上述“通过组合而非继承来扩展功能” 的设计理念,能够灵活地将健康检查、缓存、加权等多种实例处理逻辑组合起来,满足 Spring Cloud LoadBalancer 对服务实例列表的复杂处理需求。
DelegatingServiceInstanceListSupplier 内部持有一个被委托的 ServiceInstanceListSupplier 实例(称为 delegate),所有方法都会委托给 delegate 执行,并在其基础上添加额外逻辑。
下面是 DelegatingServiceInstanceListSupplier 的源码:
public abstract class DelegatingServiceInstanceListSupplier
implements ServiceInstanceListSupplier, SelectedInstanceCallback, InitializingBean, DisposableBean {
/**
* 被委托的服务实例列表供应商(装饰器模式中的"被装饰对象")。
* 所有基础功能调用都会委托给该实例,子类可在此基础上添加额外逻辑。
*/
protected final ServiceInstanceListSupplier delegate;
public DelegatingServiceInstanceListSupplier(ServiceInstanceListSupplier delegate) {
Assert.notNull(delegate, "delegate may not be null");
this.delegate = delegate;
}
/**
* 获取被委托的服务实例列表供应商。
*/
public ServiceInstanceListSupplier getDelegate() {
return delegate;
}
/**
* 获取服务ID,直接调用被委托对象的 getServiceId() 方法。
* 确保整个装饰器链中返回一致的服务标识。
*/
@Override
public String getServiceId() {
return delegate.getServiceId();
}
/**
* 实例选择回调方法,当负载均衡器选中某个实例时触发。
* 若被委托对象实现了 SelectedInstanceCallback,则将回调传递给它,
* 确保委托链中的回调逻辑能被逐层执行(如粘性会话的实例记录)。
*/
@Override
public void selectedServiceInstance(ServiceInstance serviceInstance) {
if (delegate instanceof SelectedInstanceCallback selectedInstanceCallbackDelegate) {
selectedInstanceCallbackDelegate.selectedServiceInstance(serviceInstance);
}
}
/**
* 初始化回调方法,在Bean属性设置完成后执行。
* 若被委托对象实现了 InitializingBean,则调用其初始化方法,
* 确保委托链中的初始化逻辑(如缓存初始化、健康检查器启动)能被正确执行。
* @throws Exception 初始化过程中发生的异常
*/
@Override
public void afterPropertiesSet() throws Exception {
if (delegate instanceof InitializingBean) {
((InitializingBean) delegate).afterPropertiesSet();
}
}
/**
* 销毁回调方法,在Bean销毁时执行。
* 若被委托对象实现了 DisposableBean,则调用其销毁方法,
* 确保委托链中的资源释放逻辑(如缓存清理、连接关闭)能被正确执行。
* @throws Exception 销毁过程中发生的异常
*/
@Override
public void destroy() throws Exception {
if (delegate instanceof DisposableBean) {
((DisposableBean) delegate).destroy();
}
}
}上述源码中并没有 get() 方法的身影,因为 get() 方法由子类去实现,下面以 WeightedServiceInstanceListSupplier 类为例,它支持加权的实例列表供应商,可根据实例的权重属性进行实例筛选或排序,适配加权负载均衡场景。部分源码如下:
public class WeightedServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier {
//...
@Override
public Flux<List<ServiceInstance>> get() {
// 委托给被装饰的供应商获取原始实例列表,再通过expandByWeight方法按权重扩展
return delegate.get().map(this::expandByWeight);
}
@Override
public Flux<List<ServiceInstance>> get(Request request) {
// 若配置为对委托链传递请求上下文,则先通过 delegate.get(request)
// 获取基于请求过滤的实例列表,再按权重扩展
if (callGetWithRequestOnDelegates) {
return delegate.get(request).map(this::expandByWeight);
}
// 否则直接调用无参get()方法,使用默认实例列表进行权重扩展
return get();
}
/**
* 根据实例权重对服务实例列表进行扩展,生成一个按权重比例分布的实例列表,
* 使负载均衡策略(如轮询)能按照权重概率选择实例。
*
* <p>例如:若实例A权重为2、实例B权重为1,扩展后列表中A出现2次、B出现1次,
* 轮询时A被选中的概率是B的2倍,从而实现加权负载均衡。
*
* @param instances 原始服务实例列表(未按权重处理)
* @return 按权重扩展后的实例列表(实际为 LazyWeightedServiceInstanceList 惰性实现,节省内存)
*/
private List<ServiceInstance> expandByWeight(List<ServiceInstance> instances) {
// 若原始实例列表为空,直接返回空列表(无实例可处理)
if (instances.size() == 0) {
return instances;
}
// 计算每个实例的权重值,存储到数组中
int[] weights = instances.stream().mapToInt(instance -> {
try {
// 应用权重函数获取实例的权重(权重函数可自定义,如从实例元数据中提取)
int weight = weightFunction.apply(instance);
// 若权重小于等于0,使用默认权重(DEFAULT_WEIGHT,通常为1),并记录调试日志
if (weight <= 0) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format(
"The weight of the instance %s should be a positive integer, but it got %d, using %d as default",
instance.getInstanceId(), weight, DEFAULT_WEIGHT));
}
return DEFAULT_WEIGHT;
}
// 权重合法,直接返回
return weight;
}
// 若计算权重时发生异常(如权重函数执行失败),使用默认权重并记录日志
catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format(
"Exception occurred during apply weight function to instance %s, using %d as default",
instance.getInstanceId(), DEFAULT_WEIGHT), e);
}
return DEFAULT_WEIGHT;
}
}).toArray();
// 返回一个惰性加载的加权实例列表(不实际创建重复实例,而是通过索引映射实现权重分布,优化内存)
return new LazyWeightedServiceInstanceList(instances, weights);
}
//...
}到这里,是不是已经对 ServiceInstanceListSupplier 和 DelegatingServiceInstanceListSupplier 有了清晰的认识,如果我们要自定义负载均衡策略,只需继承 DelegatingServiceInstanceListSupplier(装饰器基类),重写 get() 或 get(Request) 方法,添加筛选逻辑。例如:
/**
* 自定义服务实例列表供应商
*/
public class ProdEnvServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier {
// 接收被装饰的 ServiceInstanceListSupplier(如从服务发现获取原始实例的供应商)
public ProdEnvServiceInstanceListSupplier(ServiceInstanceListSupplier delegate) {
super(delegate);
}
/**
* 重写get()方法:对委托返回的实例列表进行过滤
*/
@Override
public Flux<List<ServiceInstance>> get() {
// 1. 调用委托的 get() 获取原始实例列表
// 2. 过滤出元数据中 env=prod 的实例
return getDelegate().get()
.map(instances -> instances.stream()
.filter(this::isProdEnv) // 自定义过滤逻辑
.collect(Collectors.toList()));
}
/**
* 支持基于请求的筛选(可选)
*/
@Override
public Flux<List<ServiceInstance>> get(Request request) {
// 若需要结合请求上下文过滤,可在此实现
return getDelegate().get(request)
.map(instances -> instances.stream()
.filter(this::isProdEnv)
.collect(Collectors.toList()));
}
/**
* 自定义过滤逻辑:检查实例元数据中是否包含env=prod
*/
private boolean isProdEnv(ServiceInstance instance) {
String env = instance.getMetadata().getOrDefault("env", "").toString();
return "prod".equals(env);
}
}DiscoveryClientServiceInstanceListSupplier 是基于服务发现客户端(DiscoveryClient)的 ServiceInstanceListSupplier 核心实现,负责从 Spring Cloud 服务发现组件(如 Eureka、Nacos、Consul 等)中获取原始服务实例列表,是整个负载均衡实例供应链的 “数据源起点”。
DiscoveryClientServiceInstanceListSupplier 作为 ServiceInstanceListSupplier 体系中的 “基础组件(ConcreteComponent)”,不依赖其他 ServiceInstanceListSupplier,直接对接 DiscoveryClient 接口获取服务实例,为后续装饰器(如健康检查、缓存、过滤)提供原始数据。
屏蔽不同服务发现组件(Eureka/Nacos)的差异,通过 DiscoveryClient 统一接口获取实例,确保负载均衡器无需关心实例的具体来源。
基于 DiscoveryClient 的实例变更通知机制,实时推送服务实例的上下线、元数据变更等信息,保证实例列表的时效性。
DiscoveryClientServiceInstanceListSupplier 通过 DiscoveryClient 从服务注册中心获取指定服务(serviceId)的所有实例,包括实例的 host、port、元数据(如版本、环境)、健康状态等信息。
部分源码:
/**
* 基于服务发现客户端(DiscoveryClient/ReactiveDiscoveryClient)的服务实例列表供应商实现。
* 作为负载均衡实例数据源的基础实现,直接从服务注册中心(如Eureka、Nacos)获取指定服务的原始
* 原始实例列表,并通过反应式流(Flux)提供实时更新的实例数据。
*
* @since Spring Cloud LoadBalancer 2.2.0
* @see ServiceInstanceListSupplier 服务实例列表供应商核心接口
* @see DiscoveryClient 阻塞式服务发现客户端接口
* @see ReactiveDiscoveryClient 反应式服务发现客户端接口
*/
public class DiscoveryClientServiceInstanceListSupplier implements ServiceInstanceListSupplier {
/**
* 配置服务发现调用超时时间的属性键,可通过该配置自定义超时
* 如 "spring.cloud.loadbalancer.service-discovery.timeout=5s"
*/
public static final String SERVICE_DISCOVERY_TIMEOUT = "spring.cloud.loadbalancer.service-discovery.timeout";
private static final Log LOG = LogFactory.getLog(DiscoveryClientServiceInstanceListSupplier.class);
/**
* 服务发现调用的超时时间,默认30秒,可通过{SERVICE_DISCOVERY_TIMEOUT}配置覆盖。
*/
private Duration timeout = Duration.ofSeconds(30);
/**
* 当前供应商对应的服务唯一标识(serviceId)。
*/
private final String serviceId;
/**
* 服务实例列表的响应式流,用于推送最新的实例数据(包括初始获取和后续更新)。
*/
private final Flux<List<ServiceInstance>> serviceInstances;
/**
* 构造方法(基于阻塞式 DiscoveryClient)。
* 初始化服务ID、超时时间,并创建从阻塞式服务发现客户端获取实例的反应式流。
*
* @param delegate 阻塞式服务发现客户端(如EurekaClient、NacosDiscoveryClient)
* @param environment 环境配置,用于获取服务ID和超时配置
*/
public DiscoveryClientServiceInstanceListSupplier(DiscoveryClient delegate, Environment environment) {
// 从环境配置中获取当前服务ID(通常由负载均衡器自动注入)
this.serviceId = environment.getProperty(PROPERTY_NAME);
// 解析超时配置(从环境变量中读取,如未配置则使用默认值)
resolveTimeout(environment);
// 创建响应式式流:延迟执行(defer)以实现每次订阅时获取最新实例
this.serviceInstances = Flux.defer(() ->
// 将阻塞式调用(delegate.getInstances)包装为Mono
Mono.fromCallable(() -> delegate.getInstances(serviceId))
)
// 配置超时:超过timeout未获取到实例则返回空列表,并记录调试日志
.timeout(timeout, Flux.defer(() -> {
logTimeout();
return Flux.just(new ArrayList<>());
}), Schedulers.boundedElastic())
// 异常处理:服务发现调用失败时返回空列表,并记录错误日志
.onErrorResume(error -> {
logException(error);
return Flux.just(new ArrayList<>());
});
}
/**
* 构造方法(基于响应式 ReactiveDiscoveryClient)。
* 初始化服务ID、超时时间,并创建从反应式服务发现客户端获取实例的反应式流。
*
* @param delegate 反应式服务发现客户端(如ReactiveEurekaClient)
* @param environment 环境配置,用于获取服务ID和超时配置
*/
public DiscoveryClientServiceInstanceListSupplier(ReactiveDiscoveryClient delegate, Environment environment) {
this.serviceId = environment.getProperty(PROPERTY_NAME);
resolveTimeout(environment);
// 基于响应式服务发现客户端创建流,直接调用其getInstances方法(返回Flux)
this.serviceInstances = Flux
.defer(() -> delegate.getInstances(serviceId)
.collectList() // 将 Flux<ServiceInstance> 转换为 Mono<List<ServiceInstance>>
.flux()
// 超时处理:与阻塞式构造方法逻辑一致
.timeout(timeout, Flux.defer(() -> {
logTimeout();
return Flux.just(new ArrayList<>());
}))
// 异常处理:与阻塞式构造方法逻辑一致
.onErrorResume(error -> {
logException(error);
return Flux.just(new ArrayList<>());
})
);
}
/**
* 获取当前供应商对应的服务ID。
*
* @return 服务唯一标识(serviceId)
*/
@Override
public String getServiceId() {
return serviceId;
}
/**
* 提供服务实例列表的反应式流。
* 每次订阅时会触发从服务发现客户端获取最新实例,支持实时推送实例变更。
*
* @return 包含服务实例列表的Flux,实例变更时会推送新列表
*/
@Override
public Flux<List<ServiceInstance>> get() {
return serviceInstances;
}
/**
* 从环境配置中解析服务发现超时时间,覆盖默认值。
* 支持的时间格式:如"5s"(5秒)、"1m"(1分钟)等(由DurationStyle解析)。
*
* @param environment 环境配置对象
*/
private void resolveTimeout(Environment environment) {
String providedTimeout = environment.getProperty(SERVICE_DISCOVERY_TIMEOUT);
if (providedTimeout != null) {
timeout = DurationStyle.detectAndParse(providedTimeout);
}
}
/**
* 记录服务发现超时日志(调试级别)。
*/
private void logTimeout() {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("获取服务 %s 的实例超时,超时时间:%s", serviceId, timeout));
}
}
/**
* 记录服务发现异常日志(错误级别)。
*
* @param error 服务发现过程中抛出的异常
*/
private void logException(Throwable error) {
if (LOG.isErrorEnabled()) {
LOG.error(String.format("获取服务 %s 的实例时发生异常", serviceId), error);
}
}
}到这里就结束了,更多关于 ServiceInstanceListSupplier 的知识,请阅读官方文档。