配置管理基础
配置管理是 Nacos 的核心功能之一,本章介绍 Nacos 配置管理的核心概念和使用方法。
配置管理概述
在传统应用中,配置通常硬编码在代码中或放在配置文件里。这种方式存在以下问题:
- 修改配置需要重新部署:每次修改配置都需要重新打包、部署应用,影响发布效率
- 多环境配置管理困难:开发、测试、生产环境的配置难以统一管理,容易出错
- 配置变更难以追踪:无法记录配置的变更历史,难以定位问题和回滚
- 配置分散:多个服务的配置分散在各处,难以集中管理和监控
- 无法动态调整:运行时无法动态调整配置,需要重启应用才能生效
Nacos 配置中心解决了这些问题,提供了集中化、动态化的配置管理能力。
核心能力
Nacos 配置中心提供以下核心能力:
- 动态配置:配置修改后实时推送到客户端,无需重启应用即可生效
- 配置版本管理:记录每次配置变更的历史版本,支持一键回滚
- 灰度发布:支持配置的灰度推送,可以先在小范围验证再全量发布
- 多环境隔离:通过命名空间实现不同环境的配置隔离,互不影响
- 配置监听:客户端可以监听配置变化,实时响应配置更新
- 配置加密:支持敏感配置的加密存储,保护敏感信息安全
配置模型
数据模型
Nacos 的配置由三元组唯一确定:
Namespace(命名空间)+ Group(分组)+ Data ID(配置集ID)
这种设计类似于 Maven 的 GAV 坐标,可以精确定位一个配置。每个维度的作用如下:
| 维度 | 作用 | 示例 |
|---|---|---|
| Namespace | 隔离不同环境或租户 | dev(开发)、test(测试)、prod(生产) |
| Group | 对配置进行逻辑分类 | DEFAULT_GROUP、DATABASE_GROUP、MQ_GROUP |
| Data ID | 配置的唯一标识 | user-service.yaml、application.properties |
┌─────────────────────────────────────────────────────────────────┐
│ Nacos 配置模型 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Namespace(命名空间) │ │
│ │ ┌───────────────────────────────────────────────────┐ │ │
│ │ │ Group(分组) │ │ │
│ │ │ ┌─────────────────────────────────────────────┐ │ │ │
│ │ │ │ Data ID(配置集ID) │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ key1=value1 │ │ │ │
│ │ │ │ key2=value2 │ │ │ │
│ │ │ │ key3=value3 │ │ │ │
│ │ │ └─────────────────────────────────────────────┘ │ │ │
│ │ └───────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
命名空间(Namespace)
命名空间用于实现多环境、多租户的配置隔离。最常见的使用场景是区分不同环境:
dev:开发环境test:测试环境prod:生产环境
分组(Group)
分组用于对配置进行逻辑分类,例如:
DEFAULT_GROUP:默认分组DATABASE_GROUP:数据库配置MQ_GROUP:消息队列配置
Data ID
Data ID 是配置集的唯一标识。在 Spring Cloud 中,Data ID 的命名格式为:
${prefix}-${spring.profiles.active}.${file-extension}
例如:user-service-dev.yaml
创建配置
通过控制台创建
- 登录 Nacos 控制台
- 进入「配置管理」->「配置列表」
- 点击「+」创建配置
- 填写配置信息:
- Data ID:配置的唯一标识
- Group:配置所属分组
- 配置格式:支持 Properties、YAML、JSON、Text、XML
- 配置内容:具体的配置内容
通过 API 创建
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs" \
-d "dataId=user-service.yaml" \
-d "group=DEFAULT_GROUP" \
-d "content=spring:
datasource:
url: jdbc:mysql://localhost:3306/user_db
username: root
password: root"
Spring Cloud 集成
添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
配置文件
在 bootstrap.yml 中配置 Nacos 配置中心:
spring:
application:
name: user-service
profiles:
active: dev
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: dev
group: DEFAULT_GROUP
file-extension: yaml
配置加载顺序
Spring Cloud Alibaba Nacos Config 会按以下顺序加载配置,后加载的配置会覆盖先加载的配置:
加载顺序(优先级从低到高):
1. 基础配置:{application}.{file-extension}
2. 环境配置:{application}-{profile}.{file-extension}
3. 共享配置:shared-configs 中配置的共享配置
4. 扩展配置:extension-configs 中配置的扩展配置
5. 本地配置:application.yml/properties(优先级最高)
配置加载原理
配置加载的详细过程:
- 应用启动:Spring Boot 应用启动,初始化 ApplicationContext
- 加载 bootstrap 配置:读取 bootstrap.yml 中的 Nacos 配置
- 连接 Nacos:与 Nacos Server 建立连接
- 加载远程配置:按顺序从 Nacos 加载各个配置
- 合并配置:将所有配置合并,后者覆盖前者
- 刷新上下文:使用合并后的配置刷新 Spring 上下文
配置覆盖规则
当多个配置中存在相同的配置项时,覆盖规则如下:
| 场景 | 覆盖规则 |
|---|---|
| 同一配置的不同版本 | 环境配置覆盖基础配置 |
| 不同 Data ID 的配置 | 后加载的覆盖先加载的 |
| 远程配置 vs 本地配置 | 本地配置优先级最高(可配置) |
| 共享配置 vs 扩展配置 | 扩展配置覆盖共享配置 |
可以通过以下方式调整优先级:
spring:
cloud:
config:
override-none: true # 本地配置不覆盖远程配置
allow-override: true # 允许远程配置被覆盖
override-system-properties: false # 系统属性不覆盖远程配置
配置示例
假设有以下配置:
Data ID: user-service.yaml # 基础配置
Data ID: user-service-dev.yaml # 环境配置
Data ID: common.yaml # 共享配置
spring:
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
# 共享配置
shared-configs:
- data-id: common.yaml
group: DEFAULT_GROUP
refresh: true
# 扩展配置
extension-configs:
- data-id: redis.yaml
group: DATABASE_GROUP
refresh: true
配置热更新
Nacos 支持配置的动态更新,配置修改后无需重启应用即可生效。这是 Nacos 配置中心的核心优势之一。
热更新原理
Nacos 配置热更新的实现原理:
- 配置监听:客户端启动时,向 Nacos Server 注册配置监听
- 长轮询/gRPC 推送:客户端与 Server 保持长连接,等待配置变更通知
- 配置变更:用户在 Nacos 控制台或通过 API 修改配置
- 推送通知:Nacos Server 检测到配置变更,向所有监听该配置的客户端推送通知
- 客户端更新:客户端收到通知后,拉取最新配置并更新本地缓存
- 触发回调:触发用户注册的配置变更回调,更新应用状态
使用 @RefreshScope
在需要动态更新的 Bean 上添加 @RefreshScope 注解。当配置发生变化时,Spring 会重新创建该 Bean,注入最新的配置值。
@RestController
@RefreshScope
public class ConfigController {
@Value("${app.config.title:Default Title}")
private String title;
@Value("${app.config.max-connections:100}")
private Integer maxConnections;
@GetMapping("/config")
public Map<String, Object> getConfig() {
Map<String, Object> result = new HashMap<>();
result.put("title", title);
result.put("maxConnections", maxConnections);
return result;
}
}
@RefreshScope 工作原理:
- Spring 容器在创建被
@RefreshScope标注的 Bean 时,会创建一个代理对象 - 代理对象持有一个目标 Bean 的引用,目标 Bean 被缓存起来
- 当配置变更时,
ContextRefresher会发布RefreshEvent事件 - 事件处理器收到事件后,清除被
@RefreshScope标注的 Bean 缓存 - 下次访问该 Bean 时,代理对象会重新创建目标 Bean,注入最新配置
使用 @NacosValue
@RestController
public class NacosConfigController {
@NacosValue(value = "${app.config.title}", autoRefreshed = true)
private String title;
@GetMapping("/nacos-config")
public String getTitle() {
return title;
}
}
配置监听
@Component
public class ConfigListener {
@NacosConfigListener(dataId = "user-service.yaml", groupId = "DEFAULT_GROUP")
public void onConfigChange(String newConfig) {
System.out.println("配置发生变化: " + newConfig);
}
}
配置版本管理
Nacos 会记录每次配置变更,支持查看历史版本和回滚。
查看历史版本
- 进入「配置管理」->「配置列表」
- 点击配置的「历史版本」
- 查看所有历史版本
回滚配置
- 进入历史版本列表
- 选择要回滚的版本
- 点击「回滚」
API 操作
# 获取配置历史版本
curl "http://127.0.0.1:8848/nacos/v1/cs/history?dataId=user-service.yaml&group=DEFAULT_GROUP&pageNo=1&pageSize=10"
# 回滚到指定版本
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=user-service.yaml&group=DEFAULT_GROUP&nid=123"
灰度发布
Nacos 支持配置的灰度发布,可以将新配置推送给部分实例进行验证。
通过控制台灰度发布
- 进入配置详情页
- 点击「灰度发布」
- 选择灰度实例(通过 IP 或标签筛选)
- 编辑灰度配置
- 发布灰度配置
灰度配置示例
# 原配置
app:
feature:
new-feature: false
# 灰度配置(仅对灰度实例生效)
app:
feature:
new-feature: true
多环境管理
使用命名空间隔离环境
# 开发环境配置
spring:
cloud:
nacos:
config:
namespace: dev
group: DEFAULT_GROUP
# 生产环境配置
spring:
cloud:
nacos:
config:
namespace: prod
group: DEFAULT_GROUP
使用 Profile 隔离环境
spring:
profiles:
active: dev
cloud:
nacos:
config:
file-extension: yaml
Nacos 会加载 user-service-dev.yaml 配置文件。
推荐方案
推荐结合命名空间和 Profile 使用:
- 使用命名空间隔离不同环境(dev/test/prod)
- 使用 Profile 区分同一环境下的不同配置(如不同的数据库)
配置快照与容灾
配置快照是 Nacos 客户端提供的重要容灾机制,能够在 Nacos Server 不可用时保证应用继续运行。
什么是配置快照
配置快照是 Nacos 客户端 SDK 在本地生成的配置副本。当客户端从 Nacos Server 获取配置后,会将配置保存到本地文件系统中。这个快照类似于 Git 中的本地 commit,也类似于缓存,但与普通缓存不同的是,配置快照没有过期时间的概念。
快照存储位置
配置快照默认存储在用户主目录下的 nacos/config 目录中:
${user.home}/nacos/config/
├── fixed-127.0.0.1_8848-xxx/ # 按服务器地址分组
│ ├── config-data/ # 配置数据目录
│ │ ├── DEFAULT_GROUP/ # 按分组组织
│ │ │ └── user-service.yaml # 具体配置文件
│ └── failover/ # 故障转移目录
└── ...
快照的工作原理
┌─────────────────────────────────────────────────────────────────┐
│ 配置快照工作原理 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 正常情况 │ │
│ │ │ │
│ │ 1. 应用启动 → 从 Nacos Server 获取配置 │ │
│ │ 2. 获取成功 → 保存到本地快照 │ │
│ │ 3. 配置变更 → 更新本地快照 │ │
│ │ │ │
│ │ ┌──────────┐ 获取配置 ┌──────────┐ │ │
│ │ │ 应用客户端 │ ────────────────> │ Nacos │ │ │
│ │ └──────────┘ │ Server │ │ │
│ │ │ └──────────┘ │ │
│ │ │ 保存快照 │ │
│ │ ▼ │ │
│ │ ┌──────────┐ │ │
│ │ │ 本地快照 │ │ │
│ │ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 故障情况 │ │
│ │ │ │
│ │ 1. Nacos Server 不可用 │ │
│ │ 2. 客户端无法连接 │ │
│ │ 3. 自动使用本地快照启动 │ │
│ │ 4. 应用正常运行,等待 Server 恢复 │ │
│ │ │ │
│ │ ┌──────────┐ 连接失败 ┌──────────┐ │ │
│ │ │ 应用客户端 │ ────────────────✕ │ Nacos │ │ │
│ │ └──────────┘ │ Server │ │ │
│ │ │ │ (不可用) │ │ │
│ │ │ 使用快照 │ │
│ │ ▼ │ │
│ │ ┌──────────┐ │ │
│ │ │ 本地快照 │ ──────────────────> 应用正常启动 │ │
│ │ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
快照的生命周期
| 阶段 | 操作 | 说明 |
|---|---|---|
| 首次获取 | 创建快照 | 客户端首次成功获取配置后,创建本地快照文件 |
| 配置更新 | 更新快照 | 服务端配置变更推送后,客户端更新本地快照 |
| 服务不可用 | 使用快照 | Nacos Server 不可用时,客户端使用快照启动 |
| 服务恢复 | 同步快照 | 服务恢复后,客户端对比 MD5,更新快照 |
配置快照相关参数
spring:
cloud:
nacos:
config:
# 快照相关配置
cache-dir: ${user.home}/nacos/config # 快照存储目录
# 是否在启动时优先使用本地快照
# true: 启动时先检查本地快照,存在则直接使用,异步检查更新
# false: 启动时必须连接服务端获取配置
first-sync-from-server: false
故障转移(Failover)
Nacos 还支持故障转移机制,允许在 Nacos Server 长期不可用时,通过手动修改本地快照来更新配置。
故障转移目录:${user.home}/nacos/config/{server}/failover/
当故障转移目录中存在配置文件时,客户端会优先使用该目录中的配置,而不是快照目录中的配置。
使用场景:
- Nacos Server 短期不可用:自动使用快照,无需干预
- Nacos Server 长期不可用:运维人员可修改 failover 目录中的配置文件,实现应急配置变更
故障转移流程:
1. 发现 Server 不可用
2. 检查 failover 目录是否有配置
3. 如果有,使用 failover 配置
4. 如果没有,使用快照配置
5. 后台持续尝试连接 Server
6. Server 恢复后,同步最新配置
容灾最佳实践
1. 定期备份快照
虽然 Nacos 客户端会自动维护快照,但建议定期备份快照目录,以防本地文件丢失:
# 备份快照目录
tar -czf nacos-snapshot-$(date +%Y%m%d).tar.gz -C ${HOME}/nacos config
# 恢复快照
tar -xzf nacos-snapshot-20240101.tar.gz -C ${HOME}/nacos
2. 监控快照状态
通过应用的健康检查端点监控快照状态:
@RestController
public class ConfigHealthController {
@Value("${spring.cloud.nacos.config.cache-dir}")
private String cacheDir;
@GetMapping("/config-health")
public Map<String, Object> configHealth() {
Map<String, Object> result = new HashMap<>();
File snapshotDir = new File(cacheDir);
result.put("snapshotDir", cacheDir);
result.put("exists", snapshotDir.exists());
result.put("lastModified", snapshotDir.lastModified());
// 检查关键配置的快照是否存在
String[] criticalConfigs = {"application.yaml", "database.yaml"};
Map<String, Boolean> snapshots = new HashMap<>();
for (String config : criticalConfigs) {
File snapshot = new File(cacheDir + "/" + config);
snapshots.put(config, snapshot.exists());
}
result.put("snapshots", snapshots);
return result;
}
}
3. 应急预案
制定配置中心故障的应急预案:
应急预案步骤:
1. 发现配置中心故障
2. 确认应用是否正常启动(使用快照)
3. 如果需要紧急修改配置:
a. 定位应用的快照目录
b. 在 failover 目录创建/修改配置文件
c. 重启应用或触发配置刷新
4. 联系运维人员恢复 Nacos Server
5. 服务恢复后,验证配置同步
4. 多环境快照隔离
不同环境的应用应该使用不同的快照目录:
# 开发环境
spring:
cloud:
nacos:
config:
cache-dir: ${user.home}/nacos/config-dev
# 生产环境
spring:
cloud:
nacos:
config:
cache-dir: ${user.home}/nacos/config-prod
快照与缓存的区别
| 特性 | 配置快照 | 普通缓存 |
|---|---|---|
| 过期时间 | 无 | 有 |
| 持久化 | 是(存储在文件系统) | 通常否(存储在内存) |
| 容灾能力 | 强(Server 不可用时仍可用) | 弱 |
| 更新机制 | Server 推送 + MD5 校验 | 定时过期刷新 |
| 主要目的 | 容灾 | 性能优化 |
配置快照不仅提供了性能优化(减少网络请求),更重要的是提供了强大的容灾能力,确保即使配置中心完全不可用,应用也能正常启动和运行。
配置最佳实践
1. 配置分类策略
合理的配置分类可以提高配置的可维护性。建议按以下维度进行分类:
按服务分类:
DEFAULT_GROUP
├── user-service.yaml # 用户服务配置
├── order-service.yaml # 订单服务配置
├── payment-service.yaml # 支付服务配置
└── common.yaml # 公共配置
按功能模块分类:
DATABASE_GROUP
├── mysql.yaml # MySQL 配置
├── redis.yaml # Redis 配置
└── mongodb.yaml # MongoDB 配置
MQ_GROUP
├── kafka.yaml # Kafka 配置
└── rocketmq.yaml # RocketMQ 配置
CACHE_GROUP
├── local-cache.yaml # 本地缓存配置
└── distributed-cache.yaml # 分布式缓存配置
配置拆分原则:
- 将频繁变动的配置与稳定配置分离
- 将敏感配置与普通配置分离
- 将环境相关配置与环境无关配置分离
2. 敏感配置加密
对于敏感配置(如数据库密码、API 密钥等),必须进行加密存储,防止信息泄露。
使用 Jasypt 加密
1. 添加依赖:
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
2. 配置加密密钥:
# 建议通过环境变量传入,不要硬编码
jasypt:
encryptor:
password: ${JASYPT_ENCRYPTOR_PASSWORD}
algorithm: PBEWithMD5AndDES
3. 加密敏感数据:
public class ConfigEncryption {
public static void main(String[] args) {
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword("your-secret-key");
encryptor.setAlgorithm("PBEWithMD5AndDES");
// 加密密码
String encrypted = encryptor.encrypt("my-db-password");
System.out.println("加密后: ENC(" + encrypted + ")");
// 解密验证
String decrypted = encryptor.decrypt(encrypted);
System.out.println("解密后: " + decrypted);
}
}
4. 在 Nacos 中使用加密配置:
spring:
datasource:
url: jdbc:mysql://localhost:3306/user_db
username: root
# 使用 ENC() 包裹加密值
password: ENC(加密后的密码)
加密最佳实践
- 密钥管理:加密密钥不要存储在代码库中,应通过环境变量或密钥管理系统传递
- 定期轮换:定期更换加密密钥和敏感配置值
- 权限控制:限制敏感配置的访问权限,只有授权人员才能查看和修改
- 审计日志:记录敏感配置的访问和修改日志
使用 Nacos 内置加密(2.2.0+)
Nacos 2.2.0+ 支持配置内容加密:
服务端配置:
# 开启配置加密
nacos.config.encryption.enabled=true
nacos.config.encryption.key=your-encryption-key
客户端配置:
spring:
cloud:
nacos:
config:
encryption:
enabled: true
key: your-encryption-key
3. 配置变更通知
配置变更后,通过以下方式通知相关人员:
- 配置变更审计日志
- 钉钉/企业微信通知
- 配置变更审批流程
4. 配置备份
定期备份 Nacos 配置:
# 导出配置
curl "http://127.0.0.1:8848/nacos/v1/cs/configs?export=true&dataId=&group=&tenant=dev" -o config.zip
# 导入配置
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?import=true&tenant=dev" -F "[email protected]"
5. 配置格式规范
推荐使用 YAML 格式,具有更好的可读性:
# 数据库配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/user_db
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
# Redis 配置
spring:
redis:
host: localhost
port: 6379
password:
database: 0
常见问题
1. 配置加载顺序问题
如果多个配置文件中有相同的配置项,后加载的会覆盖先加载的。可以通过调整配置加载顺序解决:
spring:
cloud:
nacos:
config:
extension-configs:
- data-id: base.yaml
order: 1
- data-id: override.yaml
order: 2
2. 配置不更新
检查以下几点:
- 是否添加了
@RefreshScope注解 - 配置的
refresh属性是否为true - 检查 Nacos Server 日志是否有推送记录
3. 配置加载失败
检查以下几点:
- Data ID 和 Group 是否正确
- 命名空间是否正确
- 配置格式是否正确
- 查看应用启动日志
4. 配置内容过长
Nacos 对配置内容有长度限制,如果配置过长,建议:
- 拆分为多个配置文件
- 使用配置引用
- 压缩配置内容
下一步
- 集群部署 - 学习 Nacos 集群部署