Spring Cloud 配置中心原理
配置中心解决"配置不随代码发布、环境间配置隔离、运行期可变更"三个问题。Spring Cloud Config 用 Git 作为配置存储,通过 EnvironmentRepository 抽象把远端配置变成本地 PropertySource。本文拆解 Server 端与 Client 端的核心机制。
整体架构
┌──────────┐ ┌─────────────────┐ ┌──────────────┐
│ Config │ │ Config Server │ │ Git 仓库 │
│ Client │──▶│ (Environment) │──▶│ (配置存储) │
│ (业务服务) │ │ EnvironmentRepository │ │ application.yml │
└──────────┘ └─────────────────┘ └──────────────┘- Config Server:把 Git/SVN/本地文件中的配置暴露为 HTTP 接口
- Config Client:启动时从 Server 拉取配置,合并进 Environment
- 客户端通过
bootstrap.yml(Spring Cloud 2020.0 前)或导入方式指定 Server 地址
Config Server 架设
依赖与配置
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>java
@SpringBootApplication
@EnableConfigServer // 开启配置服务器
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}yaml
# application.yml
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/company/config-repo
default-label: main # 默认分支
search-paths: '{application}' # 按服务名目录查找配置仓库结构
config-repo/
├── order-service/
│ ├── order-service.yml # 服务通用配置
│ └── order-service-prod.yml # prod 环境覆盖
├── user-service/
│ └── user-service.yml
└── application.yml # 所有服务共享配置访问接口
GET /{application}/{profile}[/{label}]
GET /{application}-{profile}.yml
GET /order-service/dev/main响应示例:
json
{
"name": "order-service",
"profiles": ["dev"],
"label": "main",
"version": "a1b2c3d",
"propertySources": [
{ "name": "config-repo/order-service.yml", "source": { "server.port": 8080 } },
{ "name": "config-repo/application.yml", "source": { "logging.level.root": "info" } }
]
}EnvironmentRepository:配置源抽象
接口体系
java
// org.springframework.cloud.config.server.environment.EnvironmentRepository
public interface EnvironmentRepository {
Environment findOne(String application, String profile, String label);
}
// 组合仓库:把多个仓库按顺序聚合
public class CompositeEnvironmentRepository implements EnvironmentRepository {
private final List<EnvironmentRepository> delegates;
public Environment findOne(String application, String profile, String label) {
// 逐个仓库查询,结果按顺序合并(后者覆盖前者)
}
}实现类
| 实现 | 配置源 |
|---|---|
| JGitEnvironmentRepository | Git 仓库(默认) |
| SvnEnvironmentRepository | SVN 仓库 |
| NativeEnvironmentRepository | 本地文件系统 |
| VaultEnvironmentRepository | HashiCorp Vault |
| AwsS3EnvironmentRepository | S3 对象存储 |
组合使用:多个仓库可以 spring.cloud.config.server.composite 聚合,实现"默认走 Git + 敏感配置走 Vault"。
Config Client 启动流程
依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>Spring Cloud 2020.0 前的启动流程(bootstrap 上下文)
旧版客户端启动时先创建 bootstrap 上下文:
启动
├─ 1. 加载 bootstrap.yml → 读取 config server 地址
├─ 2. Bootstrap 上下文创建
│ └─ ConfigServicePropertySourceLocator#locate
│ ├─ 调用 config server:GET /order-service/dev
│ └─ 把返回的 propertySources 注册到 Environment
├─ 3. 主上下文创建
│ └─ 使用已拉取的配置(占位符可解析)
└─ 4. 应用启动完成yaml
# bootstrap.yml(旧版)
spring:
cloud:
config:
uri: http://config-server:8888
name: order-service
profile: dev
label: mainSpring Cloud 2020.0 之后的启动流程(config data import)
新版不再用 bootstrap 上下文,改为 config data 导入机制:
yaml
# application.yml(新版)
spring:
config:
import: configserver:http://config-server:8888
application:
name: order-service
profiles:
active: dev启动时 Spring Boot 的 ConfigDataEnvironmentPostProcessor 处理 configserver: 前缀,调用 ConfigServerConfigDataLocationResolver 解析并拉取配置。
两种方式的对比
| 对比 | Bootstrap(旧) | Config Data Import(新) |
|---|---|---|
| 配置位置 | bootstrap.yml | application.yml 的 import |
| 实现 | 独立 bootstrap 上下文 | ConfigData 处理链 |
| 优先级 | bootstrap 配置优先 | import 配置合并进 Environment |
| 版本 | Spring Cloud 2020.0 前 | 2020.0+ |
PropertySource 加载与优先级
配置拉取后,各配置源按优先级组成 Environment:
优先级高 → 低
1. 命令行参数 / Java 系统属性
2. 本地 application.yml(服务自身配置)
3. Config Server 返回的 propertySources(远端配置)
├─ order-service-dev.yml(环境级)
├─ order-service.yml(服务级)
└─ application.yml(共享级)
4. 本地默认值Config Server 返回的多个 PropertySource 顺序:越具体优先级越高(order-service-dev.yml > order-service.yml > application.yml)。
配置刷新
手动刷新(@RefreshScope)
远端配置变更后,客户端不会自动生效,需要触发刷新:
java
@RestController
@RefreshScope // 标记需要动态刷新的 Bean
public class OrderController {
@Value("${order.discount}")
private String discount;
}bash
curl -X POST http://order-service:8080/actuator/refresh刷新原理:@RefreshScope 的 Bean 由 RefreshScope(自定义 Scope)管理,刷新时销毁并重建这些 Bean,重新绑定 @Value。
自动刷新(配合 Bus)
Config Server 变更后通过消息总线广播刷新事件,各客户端自动刷新,见 Bus 文档。
生产建议
- 配置仓库权限:Git 仓库独立于代码仓库,敏感配置加密(或走 Vault)
- 环境隔离:dev/test/prod 用 profile 区分,prod 走独立仓库分支
- 高可用:Config Server 多实例部署,前端加负载均衡
- 版本回滚:Git 天然支持配置回滚,切换 label 即可
- 本地缓存:客户端可在 Server 不可用时用上次拉取的配置启动(
spring.cloud.config.allow-override等配置)
常见问题
- 为什么配置没生效? 检查 import/bootstrap 配置、服务名与 profile 是否匹配仓库文件名、优先级是否被本地配置覆盖。
- Config Server 与 Nacos 配置中心的区别? Config 面向 Git 存储、需要自建 Server;Nacos 自带控制台、存储与推送,国内微服务更常用 Nacos。
- 刷新与重启的区别?
@RefreshScope只重建标记 Bean,重启会重新拉取全部配置;改动不影响 Bean 定义的配置建议直接重启。