配置中心 - 不用改代码就能改配置

一、配置管理的烦恼

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

场景1:数据库密码改了

以前:每个微服务都连数据库,密码改了得:

  1. 改user-service的application.yml
  2. 改order-service的application.yml
  3. 改product-service的application.yml
  4. 每个服务都得重启
  5. 生产环境还得走发布流程

太麻烦了!

场景2:双11活动配置

活动期间:

  • 商品打8折
  • 满200减30
  • 限时抢购

以前:改配置 -> 打包 -> 发布 -> 重启 活动都结束了,服务还没重启完!

场景3:不同环境不同配置

  • 开发环境:用本地数据库
  • 测试环境:用测试数据库
  • 生产环境:用生产数据库

以前:打包时切换profile,容易出错!

二、配置中心是啥?

就像公司的公告板

  • 所有配置都写在公告板上
  • 员工(微服务)每天上班先看公告板
  • 公告板改了,员工自动知道
  • 不用每个人挨个通知

配置中心的好处:

  1. 一处改,处处生效
  2. 热更新:不用重启服务
  3. 版本管理:可以回滚
  4. 权限控制:谁能改配置
  5. 环境隔离:开发、测试、生产分开

三、为啥选Nacos配置中心?

配置中心 优点 缺点 适用场景
Spring Cloud Config Spring亲儿子,集成好 需要配合Git,功能简单 简单项目
Apollo 功能强大,界面好 部署复杂,太重 大厂,复杂项目
Nacos 配置+注册一体,轻量 功能不如Apollo全 中小项目,Spring Cloud Alibaba
Consul 配置+注册+健康检查 对Spring Cloud支持一般 Go项目多

咱们选Nacos,因为

  1. 已经用Nacos做注册中心了
  2. 配置管理功能够用
  3. 跟Spring Cloud Alibaba集成好
  4. 部署简单

四、Nacos配置中心原理

1. 拉模式(Pull)

java 复制代码
// 服务启动时
1. 从Nacos拉取配置
2. 加载到Spring环境
3. 创建Bean

// 定时任务
每30秒拉取一次,检查配置变没变

2. 推模式(Push)

java 复制代码
1. 配置改了,Nacos通知服务
2. 服务收到通知,拉取新配置
3. 刷新Bean(@RefreshScope)

3. 配置结构

sql 复制代码
Data ID: user-service-dev.yaml
Group: DEFAULT_GROUP
Namespace: dev

相当于:
文件位置:dev/DEFAULT_GROUP/user-service-dev.yaml

五、开整!把配置挪到Nacos

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

步骤1:Nacos控制台加配置

  1. 访问http://localhost:8848/nacos
  2. 左边菜单:配置管理 -> 配置列表
  3. 点右上角:+

配置1:用户服务公共配置

yaml 复制代码
Data ID: user-service.yaml
Group: DEFAULT_GROUP
配置格式: YAML
配置内容:
server:
  port: 8081

spring:
  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:用户服务开发环境

yaml 复制代码
Data ID: user-service-dev.yaml
Group: DEFAULT_GROUP
配置内容:
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/user_db_dev?useSSL=false
  
logging:
  level:
    com.example: debug

配置3:用户服务生产环境

yaml 复制代码
Data ID: user-service-prod.yaml
Group: 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

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重点!必须用这个文件名):

yaml 复制代码
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(本地保留少量配置):

yaml 复制代码
# 这里只放本地特有的配置,或者默认值
spring:
  profiles:
    active: dev  # 激活的环境
  
  # 本地可以覆盖Nacos的配置
  # 但建议能放Nacos就放Nacos

3. 测试配置生效

java 复制代码
@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改折扣

商品服务有个折扣配置:

yaml 复制代码
# 在Nacos配置
sale:
  discount: 0.8  # 8折

步骤1:创建配置Bean

java 复制代码
@Component
@ConfigurationProperties(prefix = "sale")
@RefreshScope  // 重点!支持动态刷新
@Data
public class SaleConfig {
    private Double discount = 1.0;  // 默认不打折
    private Integer fullReduction = 0;  // 满减
    private Boolean flashSale = false;  // 是否限时抢购
}

步骤2:使用配置

java 复制代码
@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:测试热更新

  1. 启动服务,访问http://localhost:8083/product/config

  2. 看到折扣是0.8(8折)

  3. 不要重启服务,去Nacos控制台

  4. 修改product-service.yaml

    yaml 复制代码
    sale:
      discount: 0.7  # 改成7折
      fullReduction: 30  # 满200减30
  5. 发布

  6. 刷新页面,配置自动变成7折

七、配置的优先级(重要!)

加载顺序(后加载的覆盖先加载的):

  1. bootstrap.yml(本地,最高优先级)
  2. Nacos扩展配置(extension-configs,按数组顺序)
  3. Nacos共享配置(shared-configs)
  4. Nacos主配置(data-id: ${spring.application.name}.yaml)
  5. application.yml(本地,最低优先级)

示例配置:

yaml 复制代码
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

共享配置(所有服务都用)

yaml 复制代码
Data ID: common.yaml
内容:
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  
mybatis:
  configuration:
    map-underscore-to-camel-case: true
yaml 复制代码
# 在bootstrap.yml里
shared-configs:
  - data-id: common.yaml
    group: DEFAULT_GROUP
    refresh: true

八、多环境配置

方案1:用Namespace(命名空间)

  1. 在Nacos创建命名空间:

    • dev(开发)
    • test(测试)
    • prod(生产)
  2. 每个环境一套配置

  3. 服务启动时指定namespace

yaml 复制代码
spring:
  cloud:
    nacos:
      config:
        namespace: dev  # 开发环境
        # 或者用环境变量
        namespace: ${NACOS_NAMESPACE:dev}

方案2:用Group(分组)

yaml 复制代码
spring:
  profiles:
    active: dev
  
  cloud:
    nacos:
      config:
        group: ${spring.profiles.active}  # 根据环境选分组

方案3:用Data ID后缀

diff 复制代码
Data ID格式:${spring.application.name}-${环境}.yaml
例如:
- user-service-dev.yaml
- user-service-test.yaml
- user-service-prod.yaml
yaml 复制代码
spring:
  cloud:
    nacos:
      config:
        # 自动拼接环境
        name: ${spring.application.name}
        file-extension: yaml
        # Nacos会找:user-service-dev.yaml

九、敏感配置加密

场景:数据库密码不能明文存

步骤1:加Jasypt依赖

xml 复制代码
<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

步骤2:生成加密值

java 复制代码
@SpringBootApplication
public 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配置用加密值

yaml 复制代码
spring:
  datasource:
    password: ENC(g8N5jJYVWp9jF6Bz8n4K7Q==)  # 用ENC()包裹

步骤4:启动时传入密钥

bash 复制代码
# 方式1:启动参数
java -jar app.jar --jasypt.encryptor.password=mySecretKey

# 方式2:环境变量
export JASYPT_ENCRYPTOR_PASSWORD=mySecretKey
java -jar app.jar

# 方式3:配置文件(不安全)
# jasypt.encryptor.password=mySecretKey

十、配置的最佳实践

1. 配置分类存放

sql 复制代码
Nacos配置:
├── common.yaml(所有服务共享)
├── middleware.yaml(中间件配置)
├── user-service.yaml(用户服务配置)
├── user-service-dev.yaml(开发环境)
└── user-service-prod.yaml(生产环境)

2. 配置版本管理

  • 每次修改前备份
  • 使用配置历史功能
  • 重要配置变更要评审

3. 配置监控

java 复制代码
@Component
public class ConfigChangeListener {
    
    // 监听配置变更
    @EventListener
    public void handleRefresh(RefreshScopeRefreshedEvent event) {
        System.out.println("配置已刷新: " + new Date());
        // 发送通知
        // 记录日志
        // 刷新缓存
    }
}

4. 配置回滚

  1. Nacos控制台 -> 配置详情 -> 历史版本
  2. 选择要回滚的版本
  3. 回滚
  4. 所有服务自动生效

5. 配置检查

java 复制代码
@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. 配置不生效

原因 :加载顺序问题 解决

yaml 复制代码
# bootstrap.yml
spring:
  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. 动态刷新不生效

原因

  1. 没加@RefreshScope
  2. 配置没在Nacos里
  3. 没加spring-cloud-starter-bootstrap

解决

java 复制代码
// 1. 类上加@RefreshScope
// 2. 确保配置在Nacos
// 3. 检查依赖

3. 配置冲突

现象 :本地配置覆盖了Nacos配置 解决

yaml 复制代码
# application.yml里尽量少放配置
# 只放本地测试需要的
spring:
  cloud:
    nacos:
      config:
        override-none: true  # 本地不覆盖远程
        override-system-properties: false  # 系统属性不覆盖

4. 启动报错:找不到配置

原因 :Nacos连接失败或配置不存在 解决

yaml 复制代码
spring:
  cloud:
    nacos:
      config:
        # 允许配置不存在
        refresh-enabled: true
        # 设置超时
        timeout: 3000
        # 失败重试
        max-retry: 3
        retry-interval: 1000

十二、实际项目配置示例

完整bootstrap.yml

yaml 复制代码
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: 8081

logging:
  level:
    root: info
    com.example: debug

Nacos配置文件示例

common.yaml(所有服务共享):

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(数据库配置):

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

# Mybatis
mybatis:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

十三、今儿个总结

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

学会了啥?

  1. ✅ 配置中心的作用(一处改,处处生效)
  2. ✅ Nacos配置中心的使用
  3. ✅ 配置文件加载顺序
  4. ✅ 动态刷新(@RefreshScope)
  5. ✅ 多环境配置
  6. ✅ 配置加密
  7. ✅ 最佳实践

关键点

  1. bootstrap.yml优先加载
  2. @RefreshScope支持热更新
  3. 命名空间隔离环境
  4. 配置优先级要清楚
  5. 敏感信息要加密

十四、明儿个学啥?

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取:关注公众号: 小坏说Java ,获取本文所有示例代码、配置模板及导出工具。

明天咱学分布式事务

  • 用户下单,扣库存,这两步要一起成功或一起失败
  • 跨服务的事务怎么保证一致性
  • Seata是啥,咋用
  • 分布式事务的几种方案

明天咱解决钱不能算错的问题!💰

相关推荐
星浩AI2 小时前
深入理解 LlamaIndex:RAG 框架核心概念与实践
人工智能·后端·python
快手技术2 小时前
打破信息茧房!快手搜索多视角正样本增强引擎 CroPS 入选 AAAI 2026 Oral
后端·算法·架构
qq_12498707532 小时前
基于springboot的鸣珮乐器销售网站的设计与实现(源码+论文+部署+安装)
java·spring boot·后端·spring·毕业设计·计算机毕业设计
海南java第二人2 小时前
SpringBoot核心注解@SpringBootApplication深度解析:启动类的秘密
java·spring boot·后端
百度地图汽车版2 小时前
【智图译站】基于异步时空图卷积网络的不规则交通预测
前端·后端
qq_12498707532 小时前
基于Spring Boot的“味蕾探索”线上零食购物平台的设计与实现(源码+论文+部署+安装)
java·前端·数据库·spring boot·后端·小程序
开心就好20252 小时前
Python爬虫基础:HTTP和HTTPS协议的请求与响应过程详解
后端
悟能不能悟2 小时前
springboot怎么将事务设置为pending,等另外一个请求ok了,再做commit
spring boot·后端