Spring Cloud Commons 核心抽象
Spring Cloud 的可插拔能力来自 spring-cloud-commons:它只定义接口,不绑定任何实现。Eureka、Consul、Nacos 都是这套接口的"插件"。理解这三个核心接口,就理解了 Spring Cloud 的服务发现与负载均衡底层。
设计思想:抽象 + SPI
spring-cloud-commons(接口)
├─ DiscoveryClient 服务发现接口
├─ ServiceInstance 实例模型
├─ LoadBalancerClient 负载均衡接口
└─ LoadBalancer 客户端负载均衡(新)
│
├─ 实现:Nacos(spring-cloud-starter-alibaba-nacos-discovery)
├─ 实现:Eureka(spring-cloud-starter-netflix-eureka-client)
├─ 实现:Consul(spring-cloud-starter-consul-discovery)
└─ 实现:Zookeeper(spring-cloud-starter-zookeeper-discovery)Spring Boot 的自动装配 + Spring Factories 机制决定引入哪个 starter 就激活哪个实现;多个实现共存时,可通过配置或 @Primary 指定优先级。
ServiceInstance:实例模型
服务发现返回的"每个实例"长什么样:
java
public interface ServiceInstance {
String getServiceId(); // 服务名
String getHost(); // 主机
int getPort(); // 端口
boolean isSecure(); // 是否 HTTPS
URI getUri(); // 完整地址(scheme://host:port)
Map<String, String> getMetadata(); // 实例元数据(版本、区域、权重等)
}java
// 实现示例(Nacos)
public class NacosServiceInstance implements ServiceInstance {
private final Instance instance;
@Override public String getServiceId() { return instance.getServiceName(); }
@Override public String getHost() { return instance.getIp(); }
@Override public int getPort() { return instance.getPort(); }
@Override public Map<String, String> getMetadata() {
return instance.getMetadata(); // 版本、权重、区域等信息
}
}getMetadata() 是扩展点:灰度发布的版本标记、区域路由的 region、加权算法的 weight 都存在这里。
DiscoveryClient:服务发现接口
java
public interface DiscoveryClient {
String description(); // 实现描述
List<ServiceInstance> getInstances(String serviceId); // 某服务的全部实例
List<String> getServices(); // 已注册的全部服务
}使用方式
java
@Autowired
private DiscoveryClient discoveryClient;
List<ServiceInstance> instances = discoveryClient.getInstances("order-service");
for (ServiceInstance instance : instances) {
System.out.println(instance.getUri()); // http://10.0.0.5:8080
}实现对比
| 实现 | 拉取方式 | 本地缓存 |
|---|---|---|
| Nacos | 客户端长轮询 + Push | 有(HostReactor 缓存) |
| Eureka | 定时拉取(30s) | 有 |
| Consul | 长连接监听 | 有 |
| Zookeeper | Watcher 监听 | 有 |
getInstances 返回的是缓存中的实例列表(注册中心客户端通常本地维护实例缓存),不是每次实时查询。
负载均衡接口体系
旧体系:LoadBalancerClient(Ribbon 时代)
java
public interface LoadBalancerClient {
ServiceInstance choose(String serviceId); // 选一个实例
<T> T execute(String serviceId, LoadBalancerRequest<T> request); // 带负载均衡执行
}新体系:ReactorLoadBalancer(LoadBalancer 时代)
Spring Cloud 2020.0 起主推响应式接口:
java
public interface ReactorLoadBalancer<T> extends LoadBalancer<T> {
Mono<Response<T>> choose(Request request); // 响应式选取
}两者的桥接
BlockingLoadBalancerClient 实现旧的 LoadBalancerClient 接口,内部调用 ReactorLoadBalancer,让 OpenFeign/RestTemplate 等旧组件无缝使用新负载均衡器。
负载均衡的核心抽象
ServiceInstanceListSupplier:实例列表来源
java
public interface ServiceInstanceListSupplier {
Flux<List<ServiceInstance>> get(); // 提供实例列表(流式,可动态更新)
static ServiceInstanceListSupplierBuilder builder() { ... }
}默认实现与组合:
| 实现 | 作用 |
|---|---|
| DiscoveryClientServiceInstanceListSupplier | 从 DiscoveryClient 拉取实例 |
| ZonePreferenceServiceInstanceListSupplier | 区域优先(同 zone 优先) |
| WeightedServiceInstanceListSupplier | 按权重过滤 |
| HealthCheckServiceInstanceListSupplier | 健康检查过滤 |
| CachingServiceInstanceListSupplier | 本地缓存 + 定时刷新 |
yaml
# 组合示例:缓存 + 区域优先 + 权重
spring:
cloud:
loadbalancer:
configurations: zone-preference, weightedLoadBalancerClient 的实例选择
BlockingLoadBalancerClient#choose 流程:
choose(serviceId)
├─ 从 ServiceInstanceListSupplier 拿实例列表
├─ LoadBalancer(轮询/随机/权重)从中选一个
└─ 返回 ServiceInstanceSPI 扩展机制
Spring Factories 自动装配
各注册中心 starter 通过 META-INF/spring.factories 注册自动配置类:
properties
# nacos-discovery starter 的 spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosDiscoveryAutoConfiguration自动配置类用 @ConditionalOnMissingBean 注入 DiscoveryClient/LoadBalancer 实现:
java
@Configuration
@ConditionalOnDiscoveryEnabled
public class NacosDiscoveryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DiscoveryClient nacosDiscoveryClient(...) {
return new NacosDiscoveryClient(namingService);
}
}自定义实现(接入自己的注册中心)
只需实现两个接口 + 注册 Bean:
java
@Component
public class CustomDiscoveryClient implements DiscoveryClient {
@Override public List<ServiceInstance> getInstances(String serviceId) { ... }
@Override public List<String> getServices() { ... }
}
@Component
public class CustomServiceInstanceListSupplier implements ServiceInstanceListSupplier {
@Override public Flux<List<ServiceInstance>> get() { ... }
}LoadBalancer 的优先级规则
多个 LoadBalancer 实现并存时按以下顺序选择:
- 标注
@LoadBalancerClient(指定服务名)的配置类 - 标注
@LoadBalancerClients的全局配置类 LoadBalancerClientFactory的默认配置- 自动配置的默认实现(轮询)
常见问题
- 引入多个注册中心 starter 会怎样? 多个 DiscoveryClient 实现共存,
getInstances会聚合并去重;可通过配置排除某实现。 - 怎么接入自定义注册中心? 实现 DiscoveryClient + ServiceInstanceListSupplier,注册为 Bean 即可,业务代码零改动。
- 负载均衡实例列表为什么不是实时的? 客户端本地缓存 + 定时/推送更新,是为了避免每次请求都访问注册中心。
- metadata 有什么用? 灰度(version)、区域路由(zone)、加权(weight)都依赖实例元数据。