Spring Cloud Config 基础:搭建 Config Server 与 Config Client
说实话,配置中心这玩意儿,在微服务架构里就是个「基础设施」。你想想看,几十个微服务,每个都本地配一套数据库地址、缓存地址……改个东西得一个个重启。
我刚接手公司第一个微服务改造项目时,就踩过这个坑。当时线上数据库切主,我凌晨三点爬起来改了二十多个服务的配置文件……嗯,从那以后,我坚决要把配置集中管理。
Spring Cloud Config 是 Spring Cloud 家族里最轻量级的配置中心方案。它不依赖数据库,直接挂 Git 仓库,说白了就是一个「通过 HTTP 读配置」的服务。这一章,我带你手搭一遍。
本章我们要做什么?
- 搭建 Config Server —— 一个可以从 Git 仓库拉取配置的微服务。
- 搭建 Config Client —— 一个可以从 Config Server 动态获取配置的普通微服务。
- 验证两者的协作流程:客户端启动时拉取远程配置,并支持动态刷新。
先理清架构
先别急着写代码。Spring Cloud Config 的核心架构其实很简单,我画了张图,一目了然。
第一步:搭建 Config Server
先从服务端开始。创建一个 Spring Boot 项目,加两个关键依赖:
<!-- Config Server 核心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- 必加:不然启动会报找不到 bootstrap -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
然后写启动类,就一行注解的事:
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
配置方面,application.yml 长这样:
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: https://github.com/your-org/config-repo.git
search-paths: '{application}'
default-label: main
# 如果是私有仓库,需要配用户名密码或 SSH key
username: ${GIT_USER}
password: ${GIT_PASSWORD}
启动后,你可以先验证一下。浏览器访问:http://localhost:8888/user-service/default,如果 Git 仓库里确实有 user-service.yml,你会看到 JSON 返回值,里面包含配置内容。能拿到数据,说明服务端通了。
第二步:搭建 Config Client
客户端更简单。同样的 Spring Boot 项目,依赖只需要一个:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
配置文件的写法有讲究。注意:Config Client 的配置不能写在 application.yml 里,要写在 bootstrap.yml 里。为什么?因为 bootstrap.yml 优先加载,在应用上下文初始化之前就把远程配置拉下来了。
# bootstrap.yml
spring:
application:
name: user-service
cloud:
config:
uri: http://localhost:8888
# 默认拉取 main 分支,也可以改
label: main
---
# 不同环境切不同的 Config Server 地址
spring:
config:
activate:
on-profile: dev
cloud:
config:
uri: http://config-dev:8888
写一个简单的 Controller 验证配置是否生效:
@RestController
public class ConfigController {
@Value("${user.max-login-attempts}")
private int maxLoginAttempts;
@GetMapping("/config")
public Map<String, Object> config() {
return Map.of("maxLoginAttempts", maxLoginAttempts);
}
}
Git 仓库里对应的 user-service.yml 内容:
user:
max-login-attempts: 5
启动客户端,访问 /config,如果返回 {"maxLoginAttempts":5},说明配置中心已经跑通了。
FetchError: Cannot clone or checkout repository。检查了半天,发现是 Git 仓库 URI 写错了。建议先在本地 git clone 测试一下你的 URI 和凭证是否正确,再配到 Server 里。
验证多环境配置
Spring Cloud Config 支持按 profiles 区分配置。你在 Git 仓库放这些文件:
user-service.yml—— 公共配置,所有环境共享。user-service-dev.yml—— 开发环境覆盖。user-service-prod.yml—— 生产环境覆盖。
客户端通过 spring.profiles.active=dev 启动时,Config Server 会自动合并配置:公共 + dev 覆盖。
表格:配置文件优先级
同一个属性出现在多个文件里,谁生效?我列个表格:
| 加载顺序 | 配置来源 | 示例 |
|---|---|---|
| 1(最低) | Git 仓库中的 application.yml | 所有应用共享的默认配置 |
| 2 | Git 仓库中的 {application}.yml | user-service.yml |
| 3 | Git 仓库中的 {application}-{profile}.yml | user-service-dev.yml |
| 4(最高) | 客户端本地 application.yml | 本地覆盖远程配置(慎用) |
你想想看,这个优先级意味着什么?如果你在远程配置里设置了一个值,客户端本地又能覆盖它,那你改远程配置就没意义了。所以我建议:生产环境把客户端的覆盖功能关掉,强制所有配置走 Server。
动态刷新:不加此功能的配置中心是不完整的
现在配置能远程读了,但改完 Git 仓库后,客户端不会自动感知。你需要手动触发刷新。Spring Cloud Config 提供了 /actuator/refresh 端点。
首先,在客户端加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
然后在 bootstrap.yml 里开放端点:
management:
endpoints:
web:
exposure:
include: refresh
使用时,改了 Git 仓库配置后,向客户端发一个 POST 请求:
curl -X POST http://localhost:8081/actuator/refresh
返回一个 JSON 数组,里面列出哪些属性被刷新了。注意,被 @Value 注入的字段必须是可变的,且类上要有 @RefreshScope 注解:
@RestController
@RefreshScope
public class ConfigController {
@Value("${user.max-login-attempts}")
private int maxLoginAttempts;
// ...
}
关于刷新,我想说三点
- @RefreshScope 必须加在 Bean 上,不是加在属性上。你加错地方,刷新完值还是旧的。
- 刷新操作对当前节点有效,多实例需要逐个刷新或借助消息总线(Spring Cloud Bus,后面章节会讲)。
- 生产环境不要每次都手动刷新,配合 Git Webhooks 自动化触发,是常见做法。
避坑指南
- Bootstrap 上下文未加载:检查是否加了
spring-cloud-starter-bootstrap依赖。Spring Cloud 2020 版本之后,默认不加载 bootstrap。 - Git 仓库权限问题:私仓推荐用 SSH 方式配置密钥,比 username/password 更安全。我在项目中遇到过密码过期导致 Config Server 启动失败的情况。
- 配置值包含特殊字符:YAML 里如果有
#、!、%这些符号,记得加引号,不然解析会报错。 - 刷新后配置没变:检查 Bean 是否真的在
@RefreshScope之下。有些 Bean 是通过@Bean方法创建的,那方法所在的@Configuration类也得加@RefreshScope。
到这一步,你已经把 Spring Cloud Config 最核心的 Server-Client 通信链路搭好了。代码不长,但背后的机制值得理解:为什么用 bootstrap?为什么有优先级?为什么需要 refresh?