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 动态刷新的实现原理

动态刷新说白了就是:配置变了,应用不重启就能拿到新值。怎么做到的?

  1. 客户端轮询:每隔几秒去配置中心拉一次,对比 MD5,变了就更新本地缓存。
  2. 服务端推送:配置变更时,配置中心主动通知客户端(Apollo 用的就是这种)。
  3. 长轮询:客户端发起请求,服务端hold住连接,有变更才返回(Nacos 的做法)。
注意:不是所有配置都适合动态刷新。比如数据库连接池大小,改了之后要重建连接池,处理不好会出问题。我一般只对开关类配置(功能开关、阈值、超时时间)做动态刷新。

4.4 知识体系总览

下面这张图,把配置管理的核心脉络串起来了:

配置管理知识体系 配置来源 YAML(推荐) JSON Properties 多环境配置:dev → test → prod 配置中心:Apollo / Nacos / Spring Cloud Config 统一管理 + 版本控制 动态刷新(长轮询/推送) 权限管控 + 审计 核心原则:敏感信息不落代码,环境差异不混用,变更可追溯

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 发布功能就是干这个的。
一句话总结:配置管理的本质不是「怎么写配置文件」,而是「怎么让配置变更安全、可控、可追溯」。