一、配置管理的烦恼
场景1:数据库密码改了
以前:每个微服务都连数据库,密码改了得:
改user-service的application.yml
改order-service的application.yml
改product-service的application.yml
每个服务都得重启
生产环境还得走发布流程
太麻烦了!
场景2:双11活动配置
活动期间:
以前:改配置 -> 打包 -> 发布 -> 重启
活动都结束了,服务还没重启完!
场景3:不同环境不同配置
开发环境:用本地数据库
测试环境:用测试数据库
生产环境:用生产数据库
以前:打包时切换profile,容易出错!
二、配置中心是啥?
就像公司的公告板:
所有配置都写在公告板上
员工(微服务)每天上班先看公告板
公告板改了,员工自动知道
不用每个人挨个通知
配置中心的好处:
一处改,处处生效
热更新:不用重启服务
版本管理:可以回滚
权限控制:谁能改配置
环境隔离:开发、测试、生产分开
三、为啥选Nacos配置中心?
配置中心 | 优点 | 缺点 | 适用场景 |
|---|
Spring Cloud Config | Spring亲儿子,集成好 | 需要配合Git,功能简单 | 简单项目 |
Apollo | 功能强大,界面好 | 部署复杂,太重 | 大厂,复杂项目 |
Nacos | 配置+注册一体,轻量 | 功能不如Apollo全 | 中小项目,Spring Cloud Alibaba |
Consul | 配置+注册+健康检查 | 对Spring Cloud支持一般 | Go项目多 |
咱们选Nacos,因为:
已经用Nacos做注册中心了
配置管理功能够用
跟Spring Cloud Alibaba集成好
部署简单
四、Nacos配置中心原理
1. 拉模式(Pull)
// 服务启动时1. 从Nacos拉取配置2. 加载到Spring环境3. 创建Bean// 定时任务每30秒拉取一次,检查配置变没变
2. 推模式(Push)
1. 配置改了,Nacos通知服务2. 服务收到通知,拉取新配置3. 刷新Bean(@RefreshScope)
3. 配置结构
Data ID: user-service-dev.yamlGroup: DEFAULT_GROUPNamespace: dev相当于:文件位置:dev/DEFAULT_GROUP/user-service-dev.yaml
五、开整!把配置挪到Nacos
步骤1:Nacos控制台加配置
访问http://localhost:8848/nacos
左边菜单:配置管理 -> 配置列表
点右上角:+
配置1:用户服务公共配置
Data ID: user-service.yamlGroup: DEFAULT_GROUP配置格式: YAML配置内容:server: port: 8081spring: datasource: url: jdbc:mysql://localhost:3306/user_db?useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8
配置2:用户服务开发环境
Data ID: user-service-dev.yamlGroup: DEFAULT_GROUP配置内容:spring: datasource: url: jdbc:mysql://localhost:3306/user_db_dev?useSSL=falselogging: level: com.example: debug
配置3:用户服务生产环境
Data ID: user-service-prod.yamlGroup: DEFAULT_GROUP配置内容:spring: datasource: url: jdbc:mysql://prod-mysql:3306/user_db?useSSL=false password: ${DB_PASSWORD:强密码}logging: level: com.example: info
步骤2:改造user-service
1. 加依赖
pom.xml:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!-- 配置刷新 --><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId></dependency>
2. 加配置文件
bootstrap.yml(重点!必须用这个文件名):
spring: application: name: user-service # 服务名,一定要有! cloud: nacos: config: server-addr: localhost:8848 # Nacos地址 file-extension: yaml # 配置文件格式 group: DEFAULT_GROUP # 分组,默认就是这个 namespace: dev # 命名空间,对应环境 # 配置内容(重要!) # 1. 先加载user-service.yaml(公共配置) # 2. 再加载user-service-dev.yaml(环境配置) # 3. 最后加载本地application.yml extension-configs: - data-id: user-service.yaml group: DEFAULT_GROUP refresh: true # 是否刷新 - data-id: user-service-${spring.profiles.active}.yaml group: DEFAULT_GROUP refresh: true
application.yml(本地保留少量配置):
# 这里只放本地特有的配置,或者默认值spring: profiles: active: dev # 激活的环境 # 本地可以覆盖Nacos的配置 # 但建议能放Nacos就放Nacos
3. 测试配置生效
@RestController@RequestMapping("/config")public class ConfigController { @Value("${server.port}") private String port; @Value("${spring.datasource.url}") private String dbUrl; @Value("${logging.level.com.example}") private String logLevel; @GetMapping("/show") public Map<String, String> showConfig() { Map<String, String> config = new HashMap<>(); config.put("port", port); config.put("dbUrl", dbUrl); config.put("logLevel", logLevel); return config; }}
访问http://localhost:8081/config/show,应该显示Nacos里的配置。
六、配置动态刷新(热更新)
场景:双11改折扣
商品服务有个折扣配置:
# 在Nacos配置sale: discount: 0.8 # 8折
步骤1:创建配置Bean
@Component@ConfigurationProperties(prefix = "sale")@RefreshScope // 重点!支持动态刷新@Datapublic class SaleConfig { private Double discount = 1.0; // 默认不打折 private Integer fullReduction = 0; // 满减 private Boolean flashSale = false; // 是否限时抢购}
步骤2:使用配置
@RestController@RequestMapping("/product")public class ProductController { @Autowired private SaleConfig saleConfig; @GetMapping("/price/{originalPrice}") public Double calculatePrice(@PathVariable Double originalPrice) { // 动态读取配置 return originalPrice * saleConfig.getDiscount(); } @GetMapping("/config") public SaleConfig getConfig() { return saleConfig; }}
步骤3:测试热更新
启动服务,访问http://localhost:8083/product/config
看到折扣是0.8(8折)
不要重启服务,去Nacos控制台
修改product-service.yaml:
sale: discount: 0.7 # 改成7折 fullReduction: 30 # 满200减30
点发布
刷新页面,配置自动变成7折!
七、配置的优先级(重要!)
加载顺序(后加载的覆盖先加载的):
bootstrap.yml(本地,最高优先级)
Nacos扩展配置(extension-configs,按数组顺序)
Nacos共享配置(shared-configs)
Nacos主配置(data-id: ${spring.application.name}.yaml)
application.yml(本地,最低优先级)
示例配置:
spring: cloud: nacos: config: # 1. 先加载 common.yaml(基础配置) extension-configs[0]: data-id: common.yaml group: DEFAULT_GROUP refresh: true # 2. 再加载 middleware.yaml(中间件配置) extension-configs[1]: data-id: middleware.yaml group: DEFAULT_GROUP refresh: true # 3. 加载应用配置 # data-id: user-service.yaml(根据spring.application.name) # 4. 最后加载环境配置 # data-id: user-service-dev.yaml
共享配置(所有服务都用)
Data ID: common.yaml内容:spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8mybatis: configuration: map-underscore-to-camel-case: true
# 在bootstrap.yml里shared-configs: - data-id: common.yaml group: DEFAULT_GROUP refresh: true
八、多环境配置
方案1:用Namespace(命名空间)
在Nacos创建命名空间:
每个环境一套配置
服务启动时指定namespace
spring: cloud: nacos: config: namespace: dev # 开发环境 # 或者用环境变量 namespace: ${NACOS_NAMESPACE:dev}
方案2:用Group(分组)
spring: profiles: active: dev cloud: nacos: config: group: ${spring.profiles.active} # 根据环境选分组
方案3:用Data ID后缀
Data ID格式:${spring.application.name}-${环境}.yaml例如:- user-service-dev.yaml- user-service-test.yaml- user-service-prod.yaml
spring: cloud: nacos: config: # 自动拼接环境 name: ${spring.application.name} file-extension: yaml # Nacos会找:user-service-dev.yaml
九、敏感配置加密
场景:数据库密码不能明文存
步骤1:加Jasypt依赖
<dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.5</version></dependency>
步骤2:生成加密值
@SpringBootApplicationpublic class TestApp { public static void main(String[] args) { StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); encryptor.setPassword("mySecretKey"); // 加密密钥 String encrypted = encryptor.encrypt("123456"); System.out.println("加密后: " + encrypted); // 输出:g8N5jJYVWp9jF6Bz8n4K7Q== }}
步骤3:Nacos配置用加密值
spring: datasource: password: ENC(g8N5jJYVWp9jF6Bz8n4K7Q==) # 用ENC()包裹
步骤4:启动时传入密钥
# 方式1:启动参数java -jar app.jar --jasypt.encryptor.password=mySecretKey# 方式2:环境变量export JASYPT_ENCRYPTOR_PASSWORD=mySecretKeyjava -jar app.jar# 方式3:配置文件(不安全)# jasypt.encryptor.password=mySecretKey
十、配置的最佳实践
1. 配置分类存放
Nacos配置:├── common.yaml(所有服务共享)├── middleware.yaml(中间件配置)├── user-service.yaml(用户服务配置)├── user-service-dev.yaml(开发环境)└── user-service-prod.yaml(生产环境)
2. 配置版本管理
3. 配置监控
@Componentpublic class ConfigChangeListener { // 监听配置变更 @EventListener public void handleRefresh(RefreshScopeRefreshedEvent event) { System.out.println("配置已刷新: " + new Date()); // 发送通知 // 记录日志 // 刷新缓存 }}
4. 配置回滚
Nacos控制台 -> 配置详情 -> 历史版本
选择要回滚的版本
点回滚
所有服务自动生效
5. 配置检查
@RestController@RequestMapping("/actuator")public class ConfigCheckController { @Autowired private Environment environment; @GetMapping("/config-check") public Map<String, Object> checkConfig() { Map<String, Object> result = new HashMap<>(); // 检查必要配置 String[] requiredProps = { "spring.datasource.url", "spring.datasource.username", "spring.redis.host" }; for (String prop : requiredProps) { String value = environment.getProperty(prop); result.put(prop, value != null ? "OK" : "MISSING"); } return result; }}
十一、常见问题解决
1. 配置不生效
原因:加载顺序问题
解决:
# bootstrap.ymlspring: cloud: nacos: config: # 明确指定加载哪些配置 extension-configs[0]: data-id: common.yaml group: DEFAULT_GROUP refresh: true extension-configs[1]: data-id: ${spring.application.name}.yaml group: DEFAULT_GROUP refresh: true
2. 动态刷新不生效
原因:
没加@RefreshScope
配置没在Nacos里
没加spring-cloud-starter-bootstrap
解决:
// 1. 类上加@RefreshScope// 2. 确保配置在Nacos// 3. 检查依赖
3. 配置冲突
现象:本地配置覆盖了Nacos配置
解决:
# application.yml里尽量少放配置# 只放本地测试需要的spring: cloud: nacos: config: override-none: true # 本地不覆盖远程 override-system-properties: false # 系统属性不覆盖
4. 启动报错:找不到配置
原因:Nacos连接失败或配置不存在
解决:
spring: cloud: nacos: config: # 允许配置不存在 refresh-enabled: true # 设置超时 timeout: 3000 # 失败重试 max-retry: 3 retry-interval: 1000
十二、实际项目配置示例
完整bootstrap.yml
spring: application: name: user-service profiles: active: ${PROFILE:dev} # 从环境变量读取 cloud: nacos: config: server-addr: ${NACOS_HOST:localhost}:${NACOS_PORT:8848} username: ${NACOS_USERNAME:nacos} password: ${NACOS_PASSWORD:nacos} namespace: ${NACOS_NAMESPACE:dev} file-extension: yaml refresh-enabled: true # 共享配置(所有服务) shared-configs: - data-id: common.yaml group: COMMON_GROUP refresh: true - data-id: datasource.yaml group: MIDDLEWARE_GROUP refresh: true - data-id: redis.yaml group: MIDDLEWARE_GROUP refresh: true # 扩展配置(本服务) extension-configs: - data-id: ${spring.application.name}.yaml group: DEFAULT_GROUP refresh: true - data-id: ${spring.application.name}-${spring.profiles.active}.yaml group: DEFAULT_GROUP refresh: true# 本地默认值(开发环境方便)server: port: 8081logging: level: root: info com.example: debug
Nacos配置文件示例
common.yaml(所有服务共享):
# 应用通用配置app: version: 1.0.0 env: ${spring.profiles.active}# 日期时间格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 default-property-inclusion: non_null # 文件上传 servlet: multipart: max-file-size: 10MB max-request-size: 10MB# 日志logging: pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
datasource.yaml(数据库配置):
spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver hikari: minimum-idle: 5 maximum-pool-size: 20 idle-timeout: 30000 max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1# Mybatismybatis: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
十三、今儿个总结
学会了啥?
✅ 配置中心的作用(一处改,处处生效)
✅ Nacos配置中心的使用
✅ 配置文件加载顺序
✅ 动态刷新(@RefreshScope)
✅ 多环境配置
✅ 配置加密
✅ 最佳实践
关键点
bootstrap.yml优先加载
@RefreshScope支持热更新
命名空间隔离环境
配置优先级要清楚
敏感信息要加密
十四、明儿个学啥?
明天咱学分布式事务!
用户下单,扣库存,这两步要一起成功或一起失败
跨服务的事务怎么保证一致性
Seata是啥,咋用
分布式事务的几种方案
明天咱解决钱不能算错的问题!💰