Spring Cloud 学习与实践(6):Nacos 配置中心

文章目录

  • [Spring Cloud 学习与实践(6):Nacos 配置中心](#Spring Cloud 学习与实践(6):Nacos 配置中心)
    • [1. 本章目标](#1. 本章目标)
    • [2. 注册中心与配置中心的区别](#2. 注册中心与配置中心的区别)
    • [3. 为什么需要配置中心](#3. 为什么需要配置中心)
    • [4. 本章版本说明](#4. 本章版本说明)
    • [5. DataId、Group 与 Namespace](#5. DataId、Group 与 Namespace)
      • [5.1 DataId](#5.1 DataId)
      • [5.2 Group](#5.2 Group)
      • [5.3 Namespace](#5.3 Namespace)
    • [6. 本项目为什么同时使用 bootstrap.yml 和 application.yml](#6. 本项目为什么同时使用 bootstrap.yml 和 application.yml)
      • [6.1 普通 Spring Boot 通常只需要 application.yml](#6.1 普通 Spring Boot 通常只需要 application.yml)
      • [6.2 本项目使用的是传统 Spring Cloud bootstrap 模式](#6.2 本项目使用的是传统 Spring Cloud bootstrap 模式)
      • [6.3 application.yml 继续保存普通本地配置](#6.3 application.yml 继续保存普通本地配置)
      • [6.4 两个文件并不是必须同时存在](#6.4 两个文件并不是必须同时存在)
      • [6.5 当前项目启动链路](#6.5 当前项目启动链路)
    • [7. cloud-user 接入 Nacos Config](#7. cloud-user 接入 Nacos Config)
      • [7.1 添加依赖](#7.1 添加依赖)
    • [8. 创建 cloud-user/bootstrap.yml](#8. 创建 cloud-user/bootstrap.yml)
    • [9. 在 Nacos 中创建 cloud-user-dev.yaml](#9. 在 Nacos 中创建 cloud-user-dev.yaml)
      • [为什么 DataId 是 cloud-user-dev.yaml?](#为什么 DataId 是 cloud-user-dev.yaml?)
    • [10. 创建 ConfigController](#10. 创建 ConfigController)
    • [11. 启动时读取远程配置](#11. 启动时读取远程配置)
    • [12. 故障演练:配置变更已被监听,但接口仍返回旧值](#12. 故障演练:配置变更已被监听,但接口仍返回旧值)
    • [13. 为什么监听到变化后,@Value 仍然不刷新](#13. 为什么监听到变化后,@Value 仍然不刷新)
    • [14. 添加 @RefreshScope](#14. 添加 @RefreshScope)
    • [15. 验证动态刷新成功](#15. 验证动态刷新成功)
    • [16. Nacos 配置读取完整链路](#16. Nacos 配置读取完整链路)
      • [16.1 启动阶段链路](#16.1 启动阶段链路)
      • [16.2 当前 cloud-user 的实际定位](#16.2 当前 cloud-user 的实际定位)
      • [16.3 动态刷新链路](#16.3 动态刷新链路)
    • [17. 长轮询还是长连接](#17. 长轮询还是长连接)
      • [Nacos 1.x](#Nacos 1.x)
      • [Nacos 2.x](#Nacos 2.x)
    • [18. cloud-product 接入配置中心](#18. cloud-product 接入配置中心)
      • [18.1 添加依赖](#18.1 添加依赖)
      • [18.2 创建 bootstrap.yml](#18.2 创建 bootstrap.yml)
      • [18.3 创建 Nacos 配置](#18.3 创建 Nacos 配置)
      • [18.4 创建 ConfigController](#18.4 创建 ConfigController)
    • [19. cloud-order 接入配置中心](#19. cloud-order 接入配置中心)
      • [19.1 添加依赖](#19.1 添加依赖)
      • [19.2 创建 bootstrap.yml](#19.2 创建 bootstrap.yml)
      • [19.3 创建 Nacos 配置](#19.3 创建 Nacos 配置)
      • [19.4 创建 ConfigController](#19.4 创建 ConfigController)
    • [20. 创建 dev Namespace](#20. 创建 dev Namespace)
    • [21. 在 dev Namespace 中创建三份配置](#21. 在 dev Namespace 中创建三份配置)
    • [22. 三个服务切换到 dev Namespace](#22. 三个服务切换到 dev Namespace)
    • [23. 为什么修改 Namespace 后必须重启](#23. 为什么修改 Namespace 后必须重启)
    • [24. Namespace 环境隔离验证](#24. Namespace 环境隔离验证)
    • [25. 为什么不把重复依赖全部放入 cloud-common](#25. 为什么不把重复依赖全部放入 cloud-common)
      • [25.1 cloud-common 应保持职责单一](#25.1 cloud-common 应保持职责单一)
      • [25.2 依赖具有传递性](#25.2 依赖具有传递性)
      • [25.3 Starter 可能触发自动配置](#25.3 Starter 可能触发自动配置)
      • [25.4 推荐做法](#25.4 推荐做法)
    • [26. @Value 与 @ConfigurationProperties](#26. @Value 与 @ConfigurationProperties)
    • [27. 本章常见问题](#27. 本章常见问题)
      • [27.1 bootstrap.yml 是不是优先级最高?](#27.1 bootstrap.yml 是不是优先级最高?)
      • [27.2 为什么普通 Bean 不会自动刷新 @Value?](#27.2 为什么普通 Bean 不会自动刷新 @Value?)
      • [27.3 @RefreshScope 做了什么?](#27.3 @RefreshScope 做了什么?)
      • [27.4 Namespace 为什么要填写 ID?](#27.4 Namespace 为什么要填写 ID?)
      • [27.5 Group 当前为什么保持 DEFAULT_GROUP?](#27.5 Group 当前为什么保持 DEFAULT_GROUP?)
      • [27.6 配置中心和注册中心是否必须使用同一个 Nacos Server?](#27.6 配置中心和注册中心是否必须使用同一个 Nacos Server?)
      • [27.7 配置中心概念与链路](#27.7 配置中心概念与链路)
    • [28. 本章结论](#28. 本章结论)

Spring Cloud 学习与实践(6):Nacos 配置中心

本章目标:在 cloud-usercloud-productcloud-order 中接入 Nacos Config,理解远程配置从 Nacos Server 进入 Spring Environment 的完整链路;通过 @Value@RefreshScope 故障演练,掌握动态刷新机制;最后使用 Namespace 完成开发环境隔离。


1. 本章目标

上一章已经完成 Nacos 注册中心接入:

text 复制代码
Nacos Server 单机模式启动
cloud-user 注册到 Nacos
cloud-product 注册到 Nacos
cloud-order 注册到 Nacos
服务实例上下线观察
服务、实例、集群概念
临时实例与持久实例概念
多网卡注册 IP 问题说明

本章继续使用 Nacos,但关注另一个能力:

text 复制代码
Nacos 配置中心

本章主要完成:

text 复制代码
1. 理解注册中心与配置中心的区别
2. 为三个业务服务添加 Nacos Config 依赖
3. 使用 bootstrap.yml 配置远程配置加载信息
4. 在 Nacos 中创建 cloud-user-dev.yaml
5. 使用 @Value 读取 biz.version
6. 故意不添加 @RefreshScope,观察动态刷新不生效
7. 添加 @RefreshScope,验证无需重启即可刷新
8. 为 cloud-product 和 cloud-order 接入配置中心
9. 创建 dev Namespace
10. 将三个服务切换到 dev Namespace
11. 理解 DataId、Group、Namespace
12. 理解 Nacos 配置从发布到 Bean 刷新的完整链路

2. 注册中心与配置中心的区别

Nacos 同时提供:

text 复制代码
注册中心
配置中心

但两者解决的问题不同。

能力 作用
注册中心 管理服务实例地址、端口和健康状态
配置中心 集中管理配置,并支持动态刷新

注册中心解决的问题

text 复制代码
cloud-order 如何找到 cloud-product?
cloud-product 有几个实例?
某个实例是否健康?

配置中心解决的问题

text 复制代码
多个服务的配置如何统一管理?
修改配置后是否必须重新打包?
是否必须重启服务?
不同环境如何隔离?

一句话总结:

text 复制代码
注册中心解决"服务在哪里";
配置中心解决"服务用什么配置"。

3. 为什么需要配置中心

当前项目中,每个服务都有本地配置文件:

text 复制代码
cloud-user/src/main/resources/application.yml
cloud-product/src/main/resources/application.yml
cloud-order/src/main/resources/application.yml

如果未来有:

text 复制代码
30 个服务
每个服务部署 5 个实例

那么修改一个业务开关时,如果仍然逐个修改本地文件,就会面临:

text 复制代码
配置分散
修改麻烦
容易遗漏
需要重新打包
需要重启服务
不同环境容易混乱

配置中心的目标是:

text 复制代码
配置集中管理
        ↓
服务启动时读取远程配置
        ↓
配置修改后通知客户端
        ↓
部分配置无需重启即可生效

4. 本章版本说明

当前项目统一版本:

text 复制代码
JDK:17
Spring Boot:2.7.18
Spring Cloud:2021.0.8
Spring Cloud Alibaba:2021.0.5.0
Nacos Server:2.2.0

本章继续使用传统方式:

text 复制代码
bootstrap.yml
+
spring-cloud-starter-bootstrap

较新的 Spring Cloud 生态逐步推荐:

text 复制代码
spring.config.import

但本课程为了保持版本一致,并便于理解启动早期配置加载过程,仍然使用 bootstrap.yml


5. DataId、Group 与 Namespace

Nacos 中的一份配置,不是只靠文件名定位。

可以理解为:

text 复制代码
Namespace
    +
Group
    +
DataId

5.1 DataId

DataId 可以理解为配置文件名。

例如:

text 复制代码
cloud-user-dev.yaml

通常包含:

text 复制代码
服务名
环境
扩展名

5.2 Group

Group 可以理解为配置分组。

默认值:

text 复制代码
DEFAULT_GROUP

后续可以扩展:

text 复制代码
COMMON_GROUP
ORDER_GROUP
PRODUCT_GROUP

5.3 Namespace

Namespace 用于环境隔离。

常见划分:

text 复制代码
dev
test
prod

不同 Namespace 中可以存在相同:

text 复制代码
Group
DataId

例如:

text 复制代码
dev Namespace
└── DEFAULT_GROUP
    └── cloud-user-dev.yaml

prod Namespace
└── DEFAULT_GROUP
    └── cloud-user-dev.yaml

一句话总结:

text 复制代码
DataId 表示配置文件,
Group 表示配置分组,
Namespace 表示环境隔离。

6. 本项目为什么同时使用 bootstrap.yml 和 application.yml

6.1 普通 Spring Boot 通常只需要 application.yml

在普通 Spring Boot 项目中,最常用的配置文件是:

text 复制代码
application.yml
application-{profile}.yml

Spring Boot 会在启动时自动加载这些配置文件。

bootstrap.yml 不是普通 Spring Boot 默认等价支持的另一个主配置文件。


6.2 本项目使用的是传统 Spring Cloud bootstrap 模式

本项目为了接入 Nacos 配置中心,引入了:

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

启用传统 bootstrap 模式后,Spring Cloud 会先创建 bootstrap 上下文,再创建主应用上下文。

因此,本项目将启动早期需要使用的配置放入:

text 复制代码
bootstrap.yml

例如:

yaml 复制代码
spring:
  application:
    name: cloud-user

  profiles:
    active: dev

  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        namespace: dev-namespace-id
        group: DEFAULT_GROUP
        file-extension: yaml

这些配置用于告诉 Nacos Config 客户端:

text 复制代码
连接哪个 Nacos Server
使用哪个 Namespace
使用哪个 Group
读取哪个 DataId

6.3 application.yml 继续保存普通本地配置

例如:

yaml 复制代码
server:
  port: 9200

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/cloud_demo
    username: root
    password: xxx

这种拆分是为了让职责更清晰:

text 复制代码
bootstrap.yml:
启动早期外部配置定位信息

application.yml:
普通本地应用配置

Nacos 远程配置:
需要集中管理、环境隔离或动态刷新的配置

这是一种工程约定,不是绝对限制。


6.4 两个文件并不是必须同时存在

不要理解成:

text 复制代码
使用 Nacos Config
就必须同时存在 bootstrap.yml 和 application.yml

实际上:

text 复制代码
application.yml 可以不存在,
只要主应用没有需要单独保存的本地配置。

较新的 Spring Cloud Alibaba 还可以使用:

text 复制代码
application.yml
+
spring.config.import

直接导入 Nacos 配置,不再使用 bootstrap.yml。

因此,本项目当前使用的:

text 复制代码
bootstrap.yml
+
application.yml

只是传统 bootstrap 接入模式下较清晰的一种组织方式。


6.5 当前项目启动链路

text 复制代码
Spring Boot 启动
        ↓
Spring Cloud 创建 bootstrap 上下文
        ↓
读取 bootstrap.yml
        ↓
获得 Nacos Server、Namespace、Group、服务名等元信息
        ↓
Nacos Config 客户端拉取远程配置
        ↓
远程配置加入 Spring Environment
        ↓
创建主应用上下文
        ↓
application.yml 等本地配置参与属性解析
        ↓
业务 Bean 读取最终配置

一句话总结:

text 复制代码
bootstrap.yml 不是 Nacos 强制要求,
而是当前项目采用传统 bootstrap 模式时,
用于保存启动早期配置的一种约定文件。

7. cloud-user 接入 Nacos Config

先用 cloud-user 完成最小闭环。

7.1 添加依赖

cloud-user/pom.xml 中加入:

xml 复制代码
<!--
    Nacos Config:
    从 Nacos 配置中心读取远程配置,
    并支持配置变更监听。
-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!--
    启用 bootstrap.yml 加载阶段。
-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

原有 Discovery 依赖继续保留:

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

8. 创建 cloud-user/bootstrap.yml

位置:

text 复制代码
cloud-user
└── src/main/resources
    └── bootstrap.yml

内容:

yaml 复制代码
spring:
  application:
    # 服务名称。
    # 既用于注册中心,也参与配置中心 DataId 推导。
    name: cloud-user

  profiles:
    # 当前激活开发环境。
    active: dev

  cloud:
    nacos:
      config:
        # Nacos 配置中心地址。
        server-addr: 127.0.0.1:8848

        # 配置文件扩展名。
        file-extension: yaml

        # 当前使用默认分组。
        group: DEFAULT_GROUP

9. 在 Nacos 中创建 cloud-user-dev.yaml

进入 Nacos 控制台:

text 复制代码
配置管理
    ↓
配置列表
    ↓
创建配置

填写:

配置项
Data ID cloud-user-dev.yaml
Group DEFAULT_GROUP
配置格式 YAML

内容:

yaml 复制代码
biz:
  version: v1

如图↓

为什么 DataId 是 cloud-user-dev.yaml?

因为:

yaml 复制代码
spring:
  application:
    name: cloud-user

  profiles:
    active: dev

  cloud:
    nacos:
      config:
        file-extension: yaml

对应规则:

text 复制代码
${spring.application.name}-${spring.profiles.active}.${file-extension}

代入后:

text 复制代码
cloud-user-dev.yaml

10. 创建 ConfigController

创建:

text 复制代码
cloud-user
└── src/main/java
    └── com.example.cloud.user.controller
        └── ConfigController.java

第一阶段代码:

java 复制代码
package com.example.cloud.user.controller;

import com.example.cloud.common.result.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Nacos 配置中心测试接口。
 *
 * 当前故意不添加 @RefreshScope。
 *
 * 目标:
 * 1. 验证启动时能否读取远程配置
 * 2. 修改配置后观察普通 Bean 是否会自动刷新
 */
@RestController
@RequestMapping("/config")
public class ConfigController {

    /**
     * 从 Spring Environment 中读取 biz.version。
     *
     * local-default 是默认值。
     * 如果远程配置加载失败,接口会返回 local-default。
     */
    @Value("${biz.version:local-default}")
    private String bizVersion;

    @GetMapping("/version")
    public Result<String> version() {
        return Result.success(bizVersion);
    }
}

11. 启动时读取远程配置

启动:

text 复制代码
CloudUserApplication

访问:

http 复制代码
GET http://localhost:9200/config/version

返回:

json 复制代码
{
  "code": 0,
  "message": "success",
  "data": "v1"
}

说明:

text 复制代码
cloud-user 启动时已经成功读取 Nacos 配置

如图↓

12. 故障演练:配置变更已被监听,但接口仍返回旧值

在 Nacos 中将:

yaml 复制代码
biz:
  version: v1

修改为:

yaml 复制代码
biz:
  version: v2

如图↓

发布后,日志出现:

text 复制代码
Refresh keys changed: [biz.version]

如图 ↓

但是再次访问:

http 复制代码
GET http://localhost:9200/config/version

仍然返回:

json 复制代码
{
  "code": 0,
  "message": "success",
  "data": "v1"
}

现象总结

text 复制代码
Nacos 已经发布 v2
        ↓
客户端已经监听到 biz.version 变化
        ↓
接口仍然返回 v1

13. 为什么监听到变化后,@Value 仍然不刷新

当前 Controller 是普通 Spring Bean。

启动时:

text 复制代码
ConfigController 创建
        ↓
@Value 注入 v1

后续 Nacos 修改配置:

text 复制代码
Nacos 发布 v2
        ↓
客户端感知变化
        ↓
Spring Environment 更新
        ↓
普通 ConfigController 仍然是旧对象
        ↓
字段仍然保留 v1

核心结论:

text 复制代码
Environment 更新
≠
普通 Bean 字段自动重新注入

14. 添加 @RefreshScope

修改 ConfigController

java 复制代码
package com.example.cloud.user.controller;

import com.example.cloud.common.result.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Nacos 配置中心动态刷新测试接口。
 *
 * @RefreshScope:
 * 当配置发生变化并触发刷新事件后,
 * 当前 Bean 会重新创建。
 *
 * Bean 重新创建时,
 * @Value 会重新从 Spring Environment 读取最新配置。
 */
@RefreshScope
@RestController
@RequestMapping("/config")
public class ConfigController {

    @Value("${biz.version:local-default}")
    private String bizVersion;

    @GetMapping("/version")
    public Result<String> version() {
        return Result.success(bizVersion);
    }
}

15. 验证动态刷新成功

先重启 cloud-user

当前 Nacos 配置:

yaml 复制代码
biz:
  version: v2

访问:

http 复制代码
GET http://localhost:9200/config/version

返回:

json 复制代码
{
  "code": 0,
  "message": "success",
  "data": "v2"
}

如图↓

然后不重启服务,将 Nacos 配置改为:

yaml 复制代码
biz:
  version: v3

再次访问:

http 复制代码
GET http://localhost:9200/config/version

返回:

json 复制代码
{
  "code": 0,
  "message": "success",
  "data": "v3"
}

说明:

text 复制代码
动态刷新成功

16. Nacos 配置读取完整链路

这是本章最重要的原理部分。

16.1 启动阶段链路

text 复制代码
Spring Boot 启动
        ↓
bootstrap 上下文初始化
        ↓
读取 bootstrap.yml
        ↓
获得:
spring.application.name
spring.profiles.active
Nacos Server 地址
Group
Namespace
文件扩展名
        ↓
Nacos Config Starter 创建配置客户端
        ↓
客户端连接 Nacos Server
        ↓
根据规则定位远程配置:
Namespace + Group + DataId
        ↓
拉取配置内容
        ↓
将远程配置转换为 PropertySource
        ↓
加入 Spring Environment
        ↓
Spring 创建业务 Bean
        ↓
@Value 从 Environment 解析配置

16.2 当前 cloud-user 的实际定位

text 复制代码
Namespace:public
Group:DEFAULT_GROUP
DataId:cloud-user-dev.yaml

16.3 动态刷新链路

text 复制代码
Nacos 控制台发布配置
        ↓
Nacos Server 保存新配置
        ↓
客户端监听到配置变化
        ↓
客户端拉取最新配置
        ↓
更新 Spring Environment
        ↓
触发刷新事件
        ↓
RefreshScope 清理旧 Bean 缓存
        ↓
下次访问时重新创建 Bean
        ↓
@Value 重新读取最新值

一句话总结:

text 复制代码
Nacos Config 负责更新 Environment;
@RefreshScope 负责让 Bean 重新读取最新配置。

17. 长轮询还是长连接

学习 Nacos 配置刷新时,经常看到:

text 复制代码
长轮询

需要注意版本差异。

Nacos 1.x

常见机制:

text 复制代码
长轮询

Nacos 2.x

更强调:

text 复制代码
长连接

当前使用:

text 复制代码
Nacos Server 2.2.0

因此更准确的理解是:

text 复制代码
客户端持续订阅配置变化;
服务端配置更新后,
客户端能够及时感知并重新拉取最新内容。

不要机械地背:

text 复制代码
Nacos 配置刷新永远等于长轮询

18. cloud-product 接入配置中心

18.1 添加依赖

cloud-product/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>

18.2 创建 bootstrap.yml

yaml 复制代码
spring:
  application:
    name: cloud-product

  profiles:
    active: dev

  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: DEFAULT_GROUP

18.3 创建 Nacos 配置

text 复制代码
DataId:cloud-product-dev.yaml
Group:DEFAULT_GROUP

内容:

yaml 复制代码
biz:
  version: product-v1

18.4 创建 ConfigController

java 复制代码
package com.example.cloud.product.controller;

import com.example.cloud.common.result.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RefreshScope
@RestController
@RequestMapping("/config")
public class ConfigController {

    @Value("${biz.version:local-default}")
    private String bizVersion;

    @GetMapping("/version")
    public Result<String> version() {
        return Result.success(bizVersion);
    }
}

访问:

http 复制代码
GET http://localhost:9300/config/version

返回:

text 复制代码
product-v1

修改 Nacos 配置为:

text 复制代码
product-v2

不重启服务,再次访问,返回:

text 复制代码
product-v2

19. cloud-order 接入配置中心

19.1 添加依赖

cloud-order/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>

19.2 创建 bootstrap.yml

yaml 复制代码
spring:
  application:
    name: cloud-order

  profiles:
    active: dev

  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: DEFAULT_GROUP

19.3 创建 Nacos 配置

text 复制代码
DataId:cloud-order-dev.yaml
Group:DEFAULT_GROUP

内容:

yaml 复制代码
biz:
  version: order-v1

19.4 创建 ConfigController

java 复制代码
package com.example.cloud.order.controller;

import com.example.cloud.common.result.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RefreshScope
@RestController
@RequestMapping("/config")
public class ConfigController {

    @Value("${biz.version:local-default}")
    private String bizVersion;

    @GetMapping("/version")
    public Result<String> version() {
        return Result.success(bizVersion);
    }
}

访问:

http 复制代码
GET http://localhost:9400/config/version

返回:

text 复制代码
order-v1

20. 创建 dev Namespace

进入 Nacos 控制台:

text 复制代码
命名空间
    ↓
新建命名空间

填写:

text 复制代码
命名空间名:dev
描述:开发环境

创建后记录:

text 复制代码
Namespace ID

注意:

text 复制代码
bootstrap.yml 中填写的是 Namespace ID,
不是显示名称 dev。

21. 在 dev Namespace 中创建三份配置

切换到:

text 复制代码
dev

Namespace。

创建:

cloud-user-dev.yaml

yaml 复制代码
biz:
  version: user-dev-v1

cloud-product-dev.yaml

yaml 复制代码
biz:
  version: product-dev-v1

cloud-order-dev.yaml

yaml 复制代码
biz:
  version: order-dev-v1

Group 均为:

text 复制代码
DEFAULT_GROUP

建好后如图↓

22. 三个服务切换到 dev Namespace

cloud-user/bootstrap.yml 为例:

yaml 复制代码
spring:
  application:
    name: cloud-user

  profiles:
    active: dev

  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: DEFAULT_GROUP

        # 注意:填写 Namespace ID,不是名称。
        namespace: 你的-dev-Namespace-ID

同样修改:

text 复制代码
cloud-product/bootstrap.yml
cloud-order/bootstrap.yml

23. 为什么修改 Namespace 后必须重启

动态刷新的前提是:

text 复制代码
应用已经订阅了某一份配置

例如:

text 复制代码
Namespace:public
Group:DEFAULT_GROUP
DataId:cloud-user-dev.yaml

当修改 Namespace 时,相当于切换到另一套配置集合。

这不是普通业务值变化,而是:

text 复制代码
配置定位规则发生变化

因此需要重启服务,让 bootstrap 阶段重新执行。


24. Namespace 环境隔离验证

重启:

text 复制代码
CloudUserApplication
CloudProductApplication
CloudOrderApplication

分别访问:

http 复制代码
GET http://localhost:9200/config/version

GET http://localhost:9300/config/version

GET http://localhost:9400/config/version

返回:

text 复制代码
cloud-user      → user-dev-v1
cloud-product   → product-dev-v1
cloud-order     → order-dev-v1

说明三个服务已经成功读取:

text 复制代码
dev Namespace

中的配置。

25. 为什么不把重复依赖全部放入 cloud-common

三个业务服务都引入了很多类似依赖:

text 复制代码
spring-boot-starter-web
mybatis-plus-boot-starter
mysql-connector-java
nacos-discovery
nacos-config
spring-cloud-starter-bootstrap

看起来似乎可以全部放进:

text 复制代码
cloud-common

但不推荐。

25.1 cloud-common 应保持职责单一

当前 cloud-common 主要提供:

text 复制代码
统一返回 Result
错误码 ErrorCode
业务异常 BizException
全局异常处理器

它应该是公共基础库。

25.2 依赖具有传递性

如果 cloud-common 引入:

text 复制代码
MyBatis-Plus
MySQL Driver
Nacos Config

所有依赖 cloud-common 的模块都会被动拿到这些依赖。

例如:

text 复制代码
cloud-gateway

通常不需要 MyBatis-Plus 和 MySQL Driver。

25.3 Starter 可能触发自动配置

Spring Boot Starter 不只是普通 jar。

引入 Starter 后,可能触发:

text 复制代码
数据源初始化
Bean 自动注册
配置加载

不需要数据库的模块如果被动拿到数据库 Starter,可能出现:

text 复制代码
启动失败
依赖树混乱
无意义自动配置

25.4 推荐做法

text 复制代码
父工程:
统一版本

cloud-common:
公共代码

各业务模块:
按需引入依赖

一句话总结:

text 复制代码
版本统一放父工程;
公共代码放 cloud-common;
功能依赖由各模块按需声明。

未来如果重复依赖很多,可以考虑封装:

text 复制代码
自定义 Starter

但学习阶段暂时没有必要。


26. @Value 与 @ConfigurationProperties

本章为了直观演示,使用:

java 复制代码
@Value("${biz.version:local-default}")

当配置项很多时,不建议大量使用 @Value

例如:

yaml 复制代码
order:
  timeout-seconds: 30
  max-retry-count: 3
  auto-cancel-enabled: true

更适合使用:

java 复制代码
@ConfigurationProperties(prefix = "order")

优点:

text 复制代码
配置集中
类型明确
便于校验
更容易维护

本章使用 @Value,是因为它更适合演示动态刷新链路。


27. 本章常见问题

27.1 bootstrap.yml 是不是优先级最高?

不是。

更准确的理解:

text 复制代码
bootstrap.yml 加载得更早,
主要用于启动早期配置。

27.2 为什么普通 Bean 不会自动刷新 @Value?

因为 Bean 创建时已经注入过值。

后续 Environment 更新,并不会自动重新创建普通 Bean。

27.3 @RefreshScope 做了什么?

text 复制代码
清理旧 Bean 缓存
        ↓
下次访问时重新创建 Bean
        ↓
重新解析 @Value

27.4 Namespace 为什么要填写 ID?

因为配置客户端定位 Namespace 时使用的是:

text 复制代码
Namespace ID

不是界面显示名称。

27.5 Group 当前为什么保持 DEFAULT_GROUP?

当前服务数量少。

先保持简单,避免无意义复杂度。

27.6 配置中心和注册中心是否必须使用同一个 Nacos Server?

学习环境中通常使用同一个 Nacos Server。

但逻辑上,它们是两个独立能力。

27.7 配置中心概念与链路


28. 本章结论

本章完成:

text 复制代码
Nacos Config 依赖接入
spring-cloud-starter-bootstrap
bootstrap.yml
DataId
Group
Namespace
配置启动加载
配置动态监听
普通 Bean 刷新失败故障演练
@RefreshScope 修复
三个服务接入配置中心
dev Namespace 环境隔离
配置加载与刷新链路分析

当前项目已经具备:

text 复制代码
服务注册
配置集中管理
配置动态刷新
环境隔离

下一章将进入:

text 复制代码
第 7 章:OpenFeign 服务调用

下一章会完成:

text 复制代码
订单服务调用用户服务
订单服务调用商品服务
OpenFeign 接口定义
服务名调用
负载均衡
远程商品校验
远程库存扣减
远程调用失败排查
错误注册 IP 故障演练
Nacos 停止后的远程调用行为观察
相关推荐
两点王爷9 小时前
PostgreSQL 常用 SQL 语句与 GIS 相关函数详解
数据库·后端
两点王爷9 小时前
PostgreSQL 好用又独特的特性与空间函数
数据库
梦帮科技9 小时前
AI 音乐产品的发布工程:验证门、数据发布、回滚与生产运维纪律
数据结构·数据库·架构·node.js·音视频·动态规划·推荐算法
一只路人甲10 小时前
使用AI快速学习
学习
传奇开心果编程11 小时前
【Rust入门练中学】第2课:变量、可变性与常量
开发语言·学习·rust
TDengine (老段)11 小时前
TDengine 常见问题 TOP3
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
边境悍匪13 小时前
蜗牛学苑 Java 智能体学习 Day42|项目周开发技术汇总 1 思维导图复盘
java·开发语言·spring boot·学习·spring
Gyr013 小时前
浅刷了一遍51CTO软考题库客观题及其解析(4)
学习·软考·知识点·信息安全工程师
螺蛳粉 螺蛳粉14 小时前
MySQL 分布式集群系列 · 第五篇——全方位对比:NDB、MGR、主从复制、分库分表怎么选?
数据库·分布式·mysql
是隼人14 小时前
buuctf-pwn [NewStarCTF 2023 公开赛道]stack migration(64位栈迁移)题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门