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 停止后的远程调用行为观察
相关推荐
星恒随风1 小时前
Python 基础语法详解(3):顺序语句、条件语句和循环语句一篇讲清楚
开发语言·笔记·python·学习
lld9510271 小时前
(三)本地策略框架
java·服务器·数据库
scan7241 小时前
根据context={“query_type“: “vip“} 进行选择
数据库
零陵上将军_xdr1 小时前
API 签名防重放机制:基于 HMAC-SHA256 的设计与实现
java·学习·安全架构
数智工坊1 小时前
周志华《Machine Learning》学习笔记--第九章--聚类
笔记·学习·机器学习
Amazing_Cacao1 小时前
CFCA精品可可品鉴师初级防御战:刺破营销故事幻象,划定极其硬核的瑕疵风味物理边界
学习
Solis程序员1 小时前
亿级流量下的 Redis 计数系统设计:位图事实 + 事件聚合 + SDS 汇总
数据库·redis·缓存
爱喝水的鱼丶1 小时前
SAP-ABAP:SAP 内存管理详解:从架构到优化
开发语言·学习·架构·sap·abap·内存管理
专注VB编程开发20年1 小时前
C#,VB.NET 生成debug日志文件
服务器·数据库·c#