第 10 章:配置中心
学习目标
- 理解配置中心解决的"配置分散"问题
- 掌握 Nacos Config / Apollo / Spring Cloud Config
- 学会动态配置刷新和灰度发布
- 避免配置泄漏、配置覆盖的坑
一、为什么需要配置中心?
text
微服务时代
├── order-service
├── payment-service
├── inventory-service
└── ... 50 个服务
每个服务都有一份 application.yml
├── 数据库地址
├── Redis 地址
├── 第三方 API Key
└── 业务参数(超时时间、限流阈值)java
// ❌ 痛点:改一个参数要重启 50 个服务
public class DbConfig {
public static final String URL = "jdbc:mysql://192.168.1.10:3306/order_db";
// 改 DB 要改代码 + 重新部署
}二、Nacos Config 入门
xml
<!-- 引入依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>yaml
# bootstrap.yml(优先级高于 application.yml)
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: nacos:8848
namespace: prod
file-extension: yaml
refresh-enabled: truejava
// 1. 静态配置
@Value("${db.url}")
private String dbUrl;
// 2. 动态配置(支持热更新)
@RestController
@RefreshScope // 关键注解
public class ThresholdController {
@Value("${order.threshold:100}")
private int threshold;
@GetMapping("/threshold")
public int getThreshold() {
return threshold;
}
}⚠️ 坑 1:
@Value注入的字段改了不生效,因为没有被@RefreshScope包裹。Bean 级别的热更新。
三、命名空间与分组
text
Nacos
├── Namespace(环境隔离)
│ ├── dev
│ ├── test
│ ├── staging
│ └── prod
└── Group(应用分组)
├── ORDER_GROUP
├── PAYMENT_GROUP
└── COMMON_GROUPyaml
# Data ID 命名:服务名-环境.后缀
# order-service-dev.yaml
# order-service-prod.yaml
spring:
cloud:
nacos:
config:
namespace: prod # 命名空间
group: ORDER_GROUP # 分组java
// 不同服务共享通用配置
@Configuration
@NacosConfigurationProperties(dataId = "common.yaml", group = "COMMON_GROUP")
public class CommonConfig {
private String redisHost;
private int redisPort;
}四、Apollo 配置中心
java
// 1. 客户端注解
@Service
public class InventoryService {
@ApolloConfig
private Config config;
@Value("${inventory.stock.warning:10}")
private int warningThreshold;
}
// 2. 监听变更
@Service
public class ThresholdListener {
@ApolloConfigChangeListener
public void onChange(ConfigChangeEvent event) {
for (String key : event.changedKeys()) {
ConfigChange change = event.getChange(key);
log.info("配置变更:{} {} -> {}", key, change.getOldValue(), change.getNewValue());
}
}
}五、配置加密
yaml
# Nacos 配置(明文)
db:
password: 123456 # ❌ 敏感信息java
// 1. Nacos 配置加密 + 自定义解密
public class DecryptConfigListener implements Listener {
@Override
public void receiveConfigInfo(String configInfo) {
// 加密值:ENC(base64(...))
String decrypted = AESUtil.decrypt(configInfo);
}
}
// 2. Jasypt 加密
@Configuration
public class EncryptConfig {
@Bean
public StringEncryptor encryptor() {
PooledPBEStringEncryptor enc = new PooledPBEStringEncryptor();
enc.setPassword("master-key");
return enc;
}
}
// 3. application.yml 引用
db:
password: ENC(加密后的字符串)⚠️ 坑 2:密钥(
master-key)写进配置文件,等于没加密。密钥放环境变量或 K8s Secret,从外部注入。
六、配置灰度发布
java
// 1. Nacos 灰度:指定 IP 生效
@NacosValue(value = "${order.threshold:100}", autoRefreshed = true)
private int threshold;
// 灰度规则:10.0.0.5 这台机器先试用新值
// 在 Nacos 控制台设置灰度 IP 列表java
// 2. Apollo 灰度
GrayReleaseRule rule = new GrayReleaseRule();
rule.setAppId("order-service");
rule.setClusterName("default");
rule.setIp("10.0.0.5");
rule.setReleaseId("20240101-001");
grayReleaseManager.updateRule(rule);七、配置版本与回滚
text
配置历史
├── v1.0 阈值 100 → 订单成功率 95%
├── v2.0 阈值 500 → 订单成功率 99% ✅
└── v3.0 阈值 1000 → 订单成功率 92% ❌ 回滚到 v2.0java
// 配置版本回滚
nacosClient.publishConfig(
"order-service.yaml",
"ORDER_GROUP",
oldConfigValue // 恢复成 v2.0 内容
);⚠️ 坑 3:改配置前没记录旧值,出问题回滚需要查 git 历史。所有配置变更走 PR,自动记录 diff。
八、配置推送可靠性
yaml
# Nacos 长轮询(默认 30s)
spring:
cloud:
nacos:
config:
refresh-enabled: true
poll-timeout: 30000java
// 监控配置变更
@EventListener
public void onConfigChange(ConfigChangeEvent event) {
log.info("配置变更:{}", event.getKey());
// 业务清理缓存、重新初始化连接池
connectionPool.refresh();
}本章小结
| 组件 | 特点 |
|---|---|
| Nacos Config | 双栈,生态全,推荐 |
| Apollo | 携程开源,审计完善 |
| Spring Cloud Config | Spring 官方,Git 后端 |
| 关键点 | 建议 |
|---|---|
| 命名空间 | dev/test/staging/prod 隔离 |
| 敏感配置 | 加密 + 密钥外部存储 |
| 灰度发布 | 新配置先小范围生效 |
| 热更新 | @RefreshScope 包裹 |
动手练习
- Nacos 部署:用 Docker 起 Nacos,创建
order-service-dev.yaml试读取 - 动态刷新:写一个
@RefreshScopeBean,改 Nacos 配置观察值变化 - 配置加密:用 Jasypt 加密数据库密码,验证应用可以正常启动
- 灰度发布:开启灰度,指定一个 IP 优先使用新值,其他机器保持旧值
下一章:第 11 章:API 网关 →