4. 配置管理:从文件到动态刷新
配置管理这事儿,说简单也简单,说复杂能让你线上事故不断。我见过太多团队,开发环境跑得好好的,一上生产就崩,最后发现是配置写死了。嗯,今天咱们就把这块彻底聊透。
4.1 配置文件格式:YAML vs JSON vs Properties
先说说最基础的——配置文件怎么写。我个人习惯用 YAML,原因很简单:可读性强,层级清晰。但 JSON 和 Properties 也有自己的场景。
| 特性 | YAML | JSON | Properties |
|---|---|---|---|
| 可读性 | 高(缩进结构) | 中(括号多) | 低(扁平化) |
| 支持复杂结构 | 是(嵌套、数组) | 是(原生支持) | 否(需自定义) |
| 注释支持 | 是(#) | 否 | 是(#或!) |
| 类型推断 | 自动 | 严格 | 字符串 |
| 典型场景 | Spring Boot、K8s | 前端、API 配置 | Java 传统项目 |
我的建议:新项目优先用 YAML。但如果你团队里有人总把缩进搞错,JSON 反而是更安全的选择——至少括号不匹配时 IDE 会立刻报错。
4.2 多环境配置:dev / test / prod
你想想看,开发环境连的是本地数据库,测试环境用 mock 服务,生产环境要连主库。如果每次部署都手动改配置,不出错才怪。
多环境配置的核心思路就一句话:约定大于配置,覆盖优于修改。
4.2.1 文件命名规范
以 Spring Boot 为例,标准做法是这样的:
application.yml # 公共配置
application-dev.yml # 开发环境
application-test.yml # 测试环境
application-prod.yml # 生产环境
启动时通过 spring.profiles.active 指定环境:
java -jar app.jar --spring.profiles.active=prod
4.2.2 环境隔离的坑
我曾经踩过的坑:有次把生产数据库密码写到了公共配置里,结果测试环境也能连上生产库。还好只是只读查询,没出大事。从那以后,我强制要求:敏感信息、环境差异信息,必须放在环境专属配置里。
一个实用的分层策略:
- 公共层:应用名、日志格式、编码设置
- 环境层:数据库连接、缓存地址、第三方 API 端点
- 密钥层:密码、Token、证书(这部分后面讲配置中心时会细说)
4.3 配置中心:集中管理,动态刷新
项目大了以后,你会发现配置文件散落在各个服务里,改一个参数要重启一堆服务。这时候就需要配置中心了。
配置中心的核心价值:统一管理、版本控制、动态刷新、权限管控。
4.3.1 主流方案对比
| 方案 | 存储后端 | 动态刷新 | 学习成本 | 社区活跃度 |
|---|---|---|---|---|
| Apollo | MySQL | 实时推送 | 中 | 高(携程开源) |
| Nacos | MySQL + 内嵌 | 长轮询 | 低 | 高(阿里开源) |
| Spring Cloud Config | Git | 需配合 Bus | 低 | 中 |
| Consul | 内嵌 KV | Watch 机制 | 中 | 中 |
我个人比较推荐 Nacos,原因很简单:它把注册中心和配置中心合二为一了,少维护一个组件。而且它的命名空间 + 分组机制,天然适合多环境隔离。
4.3.2 动态刷新的实现原理
动态刷新说白了就是:配置变了,应用不重启就能拿到新值。怎么做到的?
- 客户端轮询:每隔几秒去配置中心拉一次,对比 MD5,变了就更新本地缓存。
- 服务端推送:配置变更时,配置中心主动通知客户端(Apollo 用的就是这种)。
- 长轮询:客户端发起请求,服务端hold住连接,有变更才返回(Nacos 的做法)。
注意:不是所有配置都适合动态刷新。比如数据库连接池大小,改了之后要重建连接池,处理不好会出问题。我一般只对开关类配置(功能开关、阈值、超时时间)做动态刷新。
4.4 知识体系总览
下面这张图,把配置管理的核心脉络串起来了:
4.5 实战:从本地文件迁移到 Nacos
光说不练假把式。咱们走一遍从本地配置迁移到 Nacos 的流程。
4.5.1 第一步:引入依赖
<!-- Spring Boot + Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2021.0.5.0</version>
</dependency>
4.5.2 第二步:配置 bootstrap.yml
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: 192.168.1.100:8848
namespace: dev
group: DEFAULT_GROUP
file-extension: yaml
refresh-enabled: true
4.5.3 第三步:在 Nacos 控制台创建配置
Data ID 的命名规则:{spring.application.name}-{profile}.{file-extension}
比如 order-service-dev.yaml:
server:
port: 8080
database:
url: jdbc:mysql://localhost:3306/order_dev
username: dev_user
password: dev_pass
feature:
new-payment-flow: true # 这个开关可以动态刷新
4.5.4 第四步:代码中动态获取
@RestController
@RefreshScope // 关键注解:开启动态刷新
public class FeatureController {
@Value("${feature.new-payment-flow:false}")
private boolean newPaymentFlow;
@GetMapping("/feature/payment")
public String checkFeature() {
return newPaymentFlow ? "新支付流程已开启" : "使用旧支付流程";
}
}
注意:
@RefreshScope 会重新创建 Bean,如果 Bean 里有状态(比如计数器、连接池),可能会丢失。我一般只对 Controller 和配置类加这个注解,Service 层尽量保持无状态。
4.6 配置管理的几个原则
最后,分享几条我用血泪换来的经验:
- 配置和代码分离:配置文件不要提交到 Git 仓库,用
.gitignore排除掉。可以用application.yml.example作为模板。 - 敏感信息加密:密码、密钥不要明文存储。Nacos 支持 AES 加密,Apollo 有公私钥加密方案。
- 变更可追溯:谁在什么时候改了哪个配置?配置中心都有审计日志,出了问题能回溯。
- 灰度发布配置:先改一台机器验证,没问题再全量推送。Nacos 的 Beta 发布功能就是干这个的。
一句话总结:配置管理的本质不是「怎么写配置文件」,而是「怎么让配置变更安全、可控、可追溯」。