《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/config-nacos/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labslab-44 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

《Nacos 极简入门》中,我们已经学习了如何搭建一个 Nacos 服务。如果还没有的胖友,赶紧先去简单学习下,重点是跟着该文「2. 单机部署」小节,自己搭建一个 Nacos 服务。

本文,我们来学习下如何在 Spring Boot 中,将 Nacos 作为一个配置中心,实现分布式环境下的配置管理。

友情提示:对 Nacos 作为注册中心感兴趣的胖友,可以看看《芋道 Spring Boot 注册中心 Nacos 入门》文章。

2. 快速入门

示例代码对应仓库:lab-44-nacos-config-demo

本小节,我们会在 Nacos 服务中定义配置,并使用 @ConfigurationProperties@Value 注解,读取该配置。

友情提示:如果胖友看过《芋道 Spring Boot 配置文件入门》「2. 自定义配置」小节,就会发现本小节是对标这块的内容。

如果没看过,也没关系,艿艿只是提一下而已,嘿嘿。继续往下看即可。

2.1 引入依赖

pom.xml 文件中,引入相关依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-44-nacos-config-demo</artifactId>

<dependencies>
<!-- Spring Boot Starter 基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<!-- 实现对 Nacos 作为配置中心的自动化配置 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.4</version>
</dependency>
</dependencies>

</project>

2.2 配置文件

application.yml 中,添加 Nacos 配置,如下:

nacos:
# Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类
config:
server-addr: 127.0.0.1:18848 # Nacos 服务器地址
bootstrap:
enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。
log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。
data-id: example # 使用的 Nacos 配置集的 dataId。
type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。
namespace: # 使用的 Nacos 的命名空间,默认为 null。

nacos.config 配置项,为 Nacos 作为配置中心的配置,对应 NacosConfigProperties 配置类。

  • server-addr:Nacos 服务器地址。

  • bootstrap.enable:是否开启 Nacos 配置预加载功能。默认为 false。😈 这里,我们设置为 true,保证使用 @Value@ConfigurationProperties 注解,可以读取到来自 Nacos 的配置项。

  • bootstrap.log-enable:是否开启 Nacos 支持日志级别的加载时机。默认为 false。😈 这里,我们设置为 true,保证 Spring Boot 应用的 Logger 能够使用来自 Nacos 的配置项。

  • data-id:使用的 Nacos 配置集的 dataId。😈 这里,我们设置为 example,稍后会去创建。

    FROM 《Nacos 文档 —— Nacos 概念》

    配置集
    一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。

    配置集 ID
    Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。

  • type:使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。这里,我们设置 YAML,对应 YAML 格式的配置格式。

  • group:使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。😈 这里,我们设置为 DEFAULT_GROUP,就是默认值。

    FROM 《Nacos 文档 —— Nacos 概念》

    配置分组
    Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置。

  • namespace:使用的 Nacos 的命名空间,默认为 null。😈 稍后,我们会通过 namespace 配置项,基于 Nacos 的多环境不同配置的功能。

    FROM 《Nacos 文档 —— Nacos 概念》

    命名空间
    用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。

😜 突然看到 Nacos 一大片的概念,胖友会有点懵逼。问题不大,跟着示例继续往下,会逐步有感觉的。

更多 Nacos Config Spring Boot 配置项,可以看看《nacos-spring-boot-project —— WIKI》文章。

2.3 创建 Nacos 配置集

① 打开 Nacos UI 界面的「配置列表」菜单,进入「配置管理」功能。如下图所示:配置管理

② 点击列表右上角的➕号,进入「新建配置」界面,创建一个 Nacos 配置集。输入如下内容,并点击「发布」按钮,完成创建。如下图所示:新建配置

其中,YAML 配置文件如下:

order:
pay-timeout-seconds: 120 # 订单支付超时时长,单位:秒。
create-frequency-seconds: 10 # 订单创建频率,单位:秒

2.4 OrderProperties

创建 OrderProperties 配置类,读取 order 配置项。代码如下:

@Component
@ConfigurationProperties(prefix = "order")
// @NacosConfigurationProperties(prefix = "order", dataId = "${nacos.config.data-id}", type = ConfigType.YAML)
public class OrderProperties {

/**
* 订单支付超时时长,单位:秒。
*/
private Integer payTimeoutSeconds;

/**
* 订单创建频率,单位:秒
*/
private Integer createFrequencySeconds;

// ... 省略 set/get 方法

}

  • 在类上,添加 @Component 注解,保证该配置类可以作为一个 Bean 被扫描到。
  • 在类上,添加 @ConfigurationProperties 注解,并设置 prefix = "order" 属性,这样它就可以读取前缀order 配置项,设置到配置类对应的属性上。

😈 这里,我们注释了一段 @NacosConfigurationProperties 注解的代码,该注解在功能上是对标 @ConfigurationProperties 注解,用于将 Nacos 配置注入 POJO 配置类中。为什么我们这里注释掉了呢?因为我们在「2.2 配置文件」中,设置了 nacos.config.bootstrap.enable=true,Spring Boot 应用在启动时,预加载了来自 Nacos 配置,所以可以直接使用 @ConfigurationProperties 注解即可。这样的好处,是可以更加通用,而无需和 Nacos 有耦合与依赖。

2.5 Application

创建 Application.java 类,配置 @SpringBootApplication 注解即可。代码如下:

@SpringBootApplication
// @NacosPropertySource(dataId = "example", type = ConfigType.YAML)
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

@Component
public class OrderPropertiesCommandLineRunner implements CommandLineRunner {

private final Logger logger = LoggerFactory.getLogger(getClass());

@Autowired
private OrderProperties orderProperties;

@Override
public void run(String... args) {
logger.info("payTimeoutSeconds:" + orderProperties.getPayTimeoutSeconds());
logger.info("createFrequencySeconds:" + orderProperties.getCreateFrequencySeconds());
}

}

@Component
public class ValueCommandLineRunner implements CommandLineRunner {

private final Logger logger = LoggerFactory.getLogger(getClass());

// @NacosValue(value = "${order.pay-timeout-seconds}")
@Value(value = "${order.pay-timeout-seconds}")
private Integer payTimeoutSeconds;

// @NacosValue(value = "${order.create-frequency-seconds}")
@Value(value = "${order.create-frequency-seconds}")
private Integer createFrequencySeconds;

@Override
public void run(String... args) {
logger.info("payTimeoutSeconds:" + payTimeoutSeconds);
logger.info("createFrequencySeconds:" + createFrequencySeconds);
}
}

}

① 在 Application 类上,我们注释了一段 @NacosPropertySource 注解,该注解用于声明从 Nacos 读取的配置集。为什么我们整列注释掉了呢?因为我们在「2.2 配置文件」中,通过 nacos.config.data-idnacos.config.type 等配置项,已经设置从 Nacos 读取的配置集。该配置一般用于在 Spring 应用中,使用 Nacos 作为配置中心。

② 在 OrderPropertiesCommandLineRunner 类中,我们测试了使用 @ConfigurationProperties 注解的 OrderProperties 配置类,读取 order 配置项的效果。

③ 在 ValueCommandLineRunner 类中,我们测试了使用 @Value 注解,读取 order 配置项的效果。😈 这里,我们注释了一段 @NacosValue 注解的代码,该注解在功能上是对标 @Value 注解,用于将 Nacos 配置注入属性种。为什么我们这里注释掉了呢?原因同 @NacosConfigurationProperties 注解。

友情提示:

  • @Value 注解,是 Spring 所提供。
  • @ConfigurationProperties 注解,是 Spring Boot 所提供。

下面,我们来执行 Application 的 #main(String[] args) 方法,启动 Spring Boot 应用。输出日志如下:

# 从 Nacos 中,读取 example 配置集。
2020-01-22 11:49:11.679 INFO 8430 --- [ main] c.a.b.n.c.u.NacosConfigPropertiesUtils : nacosConfigProperties : com.alibaba.boot.nacos.config.properties.NacosConfigProperties@6ff29830
2020-01-22 11:49:12.018 INFO 8430 --- [ main] c.a.b.n.config.util.NacosConfigUtils : load config from nacos, data-id is : example, group is : DEFAULT_GROUP

# ValueCommandLineRunner 输出
2020-01-22 11:49:12.745 INFO 8430 --- [ main] s.l.n.Application$ValueCommandLineRunner : payTimeoutSeconds:120
2020-01-22 11:49:12.746 INFO 8430 --- [ main] s.l.n.Application$ValueCommandLineRunner : createFrequencySeconds:120

# OrderPropertiesCommandLineRunner 输出
2020-01-22 11:49:12.746 INFO 8430 --- [ main] ication$OrderPropertiesCommandLineRunner : payTimeoutSeconds:120
2020-01-22 11:49:12.746 INFO 8430 --- [ main] ication$OrderPropertiesCommandLineRunner : createFrequencySeconds:10

  • 两个 CommandLineRunner 都读取 order 配置项成功,美滋滋。

友情提示:艿艿自己尝试了在《芋道 Spring Boot 配置文件入门》的如下小节的功能,都可以完美支持:

3. 多环境配置

示例代码对应仓库:lab-44-nacos-config-demo-profiles

《芋道 Spring Boot 配置文件入门》「6. 多环境配置」中,我们介绍如何基于 spring.profiles.active 配置项,在 Spring Boot 实现多环境的配置功能。在本小节中,我们会在该基础之上,实现结合 Nacos 的多环境配置。

实际上,Nacos 开发者已经告诉我们如何实现了,通过 Nacos namespace 命名空间。文档说明如下:

FROM 《Nacos 文档 —— Nacos 概念》

命名空间
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。

如此,我们可以通过给每个环境创建一个 Nacos namespace。然后,在每个 application-${profile}.yaml 配置文件中,配置对应的 Nacos namespace。

下面,我们来搭建一个结合 Nacos 的多环境的示例。

首先,让我们进行 Nacos 命名空间和配置集的创建。

3.1 创建 Nacos 命名空间

① 打开 Nacos UI 界面的「命名空间」菜单,进入「命名空间」功能。如下图所示:命名空间

② 点击列表右上角的「新建命名空间」按钮,弹出「新建命名空间」窗口,创建一个 dev 命名空间。输入如下内容,并点击「确定」按钮,完成创建。如下图所示:新建命名空间

③ 重复该操作,继续创建一个 prod 命名空间。最终 devprod 信息如下图:命名空间列表

3.2 创建 Nacos 配置集

① 打开 Nacos UI 界面的「配置列表」菜单,进入「配置管理」功能。如下图所示:配置管理

我们会发现,红圈部分多了 devprod 两个命名空间。

② 点击 dev 命名空间,然后点击列表右上角的➕号,进入「新建配置」界面,创建一个 Nacos 配置集。输入如下内容,并点击「发布」按钮,完成创建。如下图所示:新建配置

③ 点击 prod 命名空间,然后点击列表右上角的➕号,进入「新建配置」界面,创建一个 Nacos 配置集。输入如下内容,并点击「发布」按钮,完成创建。如下图所示:新建配置

如此,我们在 Nacos 中有 devprod 命名空间。而这两命名空间下,都有一个 example 配置集。而这两配置集都有 server.port 配置项,用于启动不同端口的服务器。😈 为什么选择 server.port 配置呢?因为 Spring Boot 项目启动后,从日志中就可以看到生效的服务器端口,嘿嘿~

然后,让我们进行 Spring Boot 示例的搭建。

3.3 引入依赖

pom.xml 文件中,引入相关依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-44-nacos-config-demo-profiles</artifactId>

<dependencies>
<!-- 实现对 SpringMVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 实现对 Nacos 作为配置中心的自动化配置 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.4</version>
</dependency>
</dependencies>

</project>

  • 引入 spring-boot-starter-web 原来的原因是,我们会使用 server.port 配置项,配置 Tomcat 的端口。

3.4 配置文件

resources 目录下,创建 2 个配置文件,对应不同的环境。如下:

  • application-dev.yaml,开发环境。

    nacos:
    # Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类
    config:
    server-addr: 127.0.0.1:18848 # Nacos 服务器地址
    bootstrap:
    enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。
    log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。
    data-id: example # 使用的 Nacos 配置集的 dataId。
    type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。
    group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。
    namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # 使用的 Nacos 的命名空间,默认为 null。

    • 「2.2 配置文件」不同的点,是修改了 nacos.config.namespace 配置项,设置为 dev 命名空间的编号。
  • application-prod.yaml,生产环境。

    nacos:
    # Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类
    config:
    server-addr: 127.0.0.1:18848 # Nacos 服务器地址
    bootstrap:
    enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。
    log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。
    data-id: example # 使用的 Nacos 配置集的 dataId。
    type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。
    group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。
    namespace: f1686f3b-a984-4cdf-8298-7caee3455d14 # 使用的 Nacos 的命名空间,默认为 null。

另外,我们会创建 application.yaml 配置文件,放不同环境的相同配置。例如说,spring.application.name 配置项,肯定是相同的啦。配置如下:

spring:
application:
name: demo-application

3.5 ProfilesApplication

创建 ProfilesApplication.java 类,配置 @SpringBootApplication 注解即可。代码如下:

@SpringBootApplication
public class ProfilesApplication {

public static void main(String[] args) {
SpringApplication.run(ProfilesApplication.class, args);
}

}

3.6 简单测试

下面,我们使用命令行参数进行 --spring.profiles.active 配置项,实现不同环境,读取不同配置文件。

开发环境示例:直接在 IDEA 中,增加 --spring.profiles.active=dev 到 Program arguments 中。如下图所示:IDEA 配置 - dev

启动 Spring Boot 应用,输出日志如下:

# 省略其它日志...
2020-01-22 19:22:26.038 INFO 13059 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8081 (http)

  • Tomcat 启动在 8081 端口,符合读取 dev 命名空间的 example 配置集。

生产环境示例:直接在 IDEA 中,增加 --spring.profiles.active=prod 到 Program arguments 中。如下图所示:IDEA 配置 - prod

启动 Spring Boot 应用,输出日志如下:

# 省略其它日志...
2020-01-22 19:26:33.527 INFO 13162 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8084 (http)

  • Tomcat 启动在 8084 端口,符合读取 prod 命名空间的 example 配置集。

另外,关于 Spring Boot 应用的多环境部署,胖友也可以看看《芋道 Spring Boot 持续交付 Jenkins 入门》文章。

4. 自动刷新配置

示例代码对应仓库:lab-44-nacos-config-demo-auto-refresh

在上面的示例中,我们已经实现从 Nacos 读取配置。那么,在应用已经启动的情况下,如果我们将读取的 Nacos 的配置进行修改时,应用是否会自动刷新本地的配置呢?

如果是我们上面的示例,答案是不会。不过呢,Nacos 提供了上述功能,也就是自动刷新配置。下面,我们来搭建一个自动刷新配置的示例。

4.1 引入依赖

pom.xml 文件中,引入相关依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-44-nacos-config-demo-auto-refresh</artifactId>

<dependencies>
<!-- 实现对 SpringMVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 实现对 Nacos 作为配置中心的自动化配置 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.4</version>
</dependency>
</dependencies>

</project>

  • 引入 spring-boot-starter-web 依赖的原因,稍后我们会编写 API 接口,查看来自 Nacos 配置的最新值。

4.2 创建 Nacos 配置集

在 Nacos 中,创建一个用于测试自动刷新配置的配置集 example-auto-refresh,具体内容如下图:创建 Nacos 配置集

4.3 配置文件

application.yml 中,添加 Nacos 配置,如下:

nacos:
# Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类
config:
server-addr: 127.0.0.1:18848 # Nacos 服务器地址
bootstrap:
enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。
log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。
data-id: example-auto-refresh # 使用的 Nacos 配置集的 dataId。
type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。
namespace: # 使用的 Nacos 的命名空间,默认为 null。
auto-refresh: true # 是否自动刷新,默认为 false。

  • 「2.2 配置文件」的差异点,是设置 nacos.config.auto-refresh 配置项为 true,开启 Nacos 自动刷新配置的功能。

4.4 TestProperties

cn.iocoder.springboot.lab44.nacosdemo.properties 包下,创建 TestProperties 配置类,读取 test 配置项。代码如下:

@Component
@NacosConfigurationProperties(prefix = "", dataId = "${nacos.config.data-id}", type = ConfigType.YAML, autoRefreshed = true)
public class TestProperties {

/**
* 测试属性
*/
private String test;

// ... 省略 set/get 方法

}

  • 不同于「2.4 OrderProperties」,因为我们要使用自动刷新配置的功能,必须使用 @NacosConfigurationProperties 注解。

这里有一点要注意,nacos.config.auto-refresh 配置项开启的是全局的,必须为 true 时,才能使用自动刷新配置的功能。同时,每个 @NacosConfigurationProperties@NacosValue 注解,也需要设置 autoRefreshed 属性为 true 时,对应的配置项才会自动刷新。

4.5 DemoController

cn.iocoder.springboot.lab44.nacosdemo.controller 包下,创建 DemoController 类,提供返回配置的 API 接口。代码如下:

@RestController
@RequestMapping("/demo")
public class DemoController {

// @Value("${test}")
@NacosValue(value = "${test}", autoRefreshed = true)
private String test;

@GetMapping("/test")
public String test() {
return test;
}

@Autowired
private TestProperties testProperties;

@GetMapping("/test_properties")
public TestProperties testProperties() {
return testProperties;
}

}

  • /demo/test 接口,测试 @NacosValue 注解。注意,这里为了实现自动刷新配置的功能,我们也无法使用 @Value 注解,而是使用 @NacosValue 替代。同时,设置其 autoRefreshed 属性为 true
  • /demo/test_properties 接口,测试 @NacosConfigurationProperties 注解的 TestProperties 配置类。

4.6 Application

创建 Application.java 类,配置 @SpringBootApplication 注解即可。代码如下:

@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

}

4.7 简单测试

启动 Spring Boot 应用,开始我们本轮的测试。

① 分别请求 /demo/test/demo/test_properties 接口,响应结果如下:

# /demo/test 接口
芋艿

# /demo/test_properties 接口
{
"test": "芋艿"
}

② 修改 Nacos 配置集 example-auto-refresh,将 test 配置项设置为 好帅。如下图所示:修改 Nacos 配置集

并且,我们可以看到控制台会输出大一堆 Nacos 的日志,如下图所示:日志

③ 分别请求 /demo/test/demo/test_properties 接口,响应结果如下:

# /demo/test 接口
好帅

# /demo/test_properties 接口
{
"test": "好帅"
}

  • 自动刷新配置成功。

4.8 Nacos 配置监听器

通过 @NacosValue@NacosConfigurationProperties 注解,已经能够满足我们绝大多数场景下的自动刷新配置的功能。但是,在一些场景下,我们仍然需要自定义 Nacos 配置监听器,实现对 Nacos 配置的监听,执行自定义的逻辑。

例如说,当数据库连接的配置发生变更时,我们需要通过监听该配置的变更,重新初始化应用中的数据库连接,从而访问到新的数据库地址。

又例如说,当日志级别发生变更时,我们需要通过监听该配置的变更,设置应用中的 Logger 的日志级别,从而后续的日志打印可以根据新的日志级别。

可能这么说,胖友会觉得有点抽象,我们来搭建一个日志级别的示例。

cn.iocoder.springboot.lab44.nacosdemo.listener 包下,创建 LoggingSystemConfigListener 类,监听 logging.level 配置项的变更,修改 Logger 的日志级别。代码如下:

@Component
public class LoggingSystemConfigListener {

/**
* 日志配置项的前缀
*/
private static final String LOGGER_TAG = "logging.level.";

@Resource
private LoggingSystem loggingSystem;

@NacosConfigListener(dataId = "${nacos.config.data-id}", type = ConfigType.YAML, timeout = 5000)
public void onChange(String newLog) throws Exception {
// <X> 使用 DefaultYamlConfigParse 工具类,解析配置
Properties properties = new DefaultYamlConfigParse().parse(newLog);
// <Y> 遍历配置集的每个配置项,判断是否是 logging.level 配置项
for (Object t : properties.keySet()) {
String key = String.valueOf(t);
// 如果是 logging.level 配置项,则设置其对应的日志级别
if (key.startsWith(LOGGER_TAG)) {
// 获得日志级别
String strLevel = properties.getProperty(key, "info");
LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
// 设置日志级别到 LoggingSystem 中
loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level);
}
}
}

}

  • loggingSystem 属性,是 Spring Boot Logger 日志系统,通过 LoggingSystem 可以进行日志级别的修改。
  • #onChange(String newLog) 方法上,我们添加了 @NacosConfigListener 注解,声明该方法处理指定配置集的配置变化。
  • <X> 处,使用 Nacos 提供的 DefaultYamlConfigParse 解析 YAML 格式的配置。示例如下图所示:DefaultYamlConfigParse
  • <Y> 处,遍历配置集的每个配置项,判断如果是 logging.level 配置项,则设置到 LoggingSystem 中,从而修改日志级别。详细的整个过程,胖友看看艿艿的详细的注释,嘿嘿~

4.9 再次测试

① 在 DemoController 类中,增加如下 API 接口。代码如下:

private Logger logger = LoggerFactory.getLogger(getClass());

@GetMapping("/logger")
public void logger() {
logger.debug("[logger][测试一下]");
}

  • 如果 DemoController 对应的 Logger 日志级别是 DEBUG 以上,则无法打印出日志。

② 在 Nacos 中,修改测试自动刷新配置的配置集 example-auto-refresh,具体内容如下图:修改 Nacos 配置集

其中,配置内容如下,方便胖友复制:

test: 好帅

logging:
level:
cn:
iocoder:
springboot:
lab44:
nacosdemo:
controller:
DemoController: INFO

③ 启动 Spring Boot 应用,开始我们本轮的测试。

④ 请求 /demo/logger 接口,控制台并未打印日志,因为当前日志级别是 INFO。

⑤ 在 Nacos 中,修改测试自动刷新配置的配置集 example-auto-refresh,具体内容如下图:修改 Nacos 配置集 02

⑥ 请求 /demo/logger 接口,控制台打印日志,因为当前日志级别是 DEBUG。日志内容如下:

2020-01-23 14:40:41.484 DEBUG 23501 --- [nio-8080-exec-5] c.i.s.l.n.controller.DemoController      : [logger][测试一下]

  • 符合预期。

🐶 感兴趣的胖友,也可以在 LoggingSystemConfigListener 的监听方法增加断点,嘿嘿~

5. 配置加密

示例代码对应仓库:lab-44-nacos-config-demo-jasypt

考虑到安全性,我们可能最好将配置文件中的敏感信息进行加密。例如说,MySQL 的用户名密码、第三方平台的 Token 令牌等等。不过,Nacos 暂时未内置配置加密的功能。官方文档说明如下:

FROM https://nacos.io/zh-cn/docs/faq.html

Nacos如何对配置进行加密
Nacos 计划在 1.X 版本提供加密的能力,目前还不支持加密,只能靠 sdk 做好了加密再存到 nacos 中。

因此,我们暂时只能在客户端进行配置的加解密。这里,我们继续采用在《芋道 Spring Boot 配置文件入门》「8. 配置加密」小节中使用的 Jasypt

下面,我们来使用 Nacos + Jasypt 搭建一个配置加密的示例。

5.1 引入依赖

pom.xml 文件中,引入相关依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-44-nacos-config-demo-jasypt</artifactId>

<dependencies>
<!-- 实现对 SpringMVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 实现对 Nacos 作为配置中心的自动化配置 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.4</version>
</dependency>

<!-- 实现对 Jasypt 实现自动化配置 -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.2</version>
<!-- <scope>test</scope>-->
</dependency>

<!-- 方便等会写单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>

5.2 创建 Nacos 配置集

在 Nacos 中,创建一个用于测试配置加密的配置集 example-jasypt,具体内容如下图:创建 Nacos 配置集

这里为了测试简便,我们直接添加加密秘钥 jasypt.encryptor.password 配置项在该 Nacos 配置集中。如果为了安全性更高,实际建议把加密秘钥和配置隔离。不然,如果配置泄露,岂不是可以拿着加密秘钥,直接进行解密。

5.3 配置文件

application.yml 中,添加 Nacos 配置,如下:

nacos:
# Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类
config:
server-addr: 127.0.0.1:18848 # Nacos 服务器地址
bootstrap:
enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。
log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。
data-id: example-jasypt # 使用的 Nacos 配置集的 dataId。
type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。
namespace: # 使用的 Nacos 的命名空间,默认为 null。
auto-refresh: true # 是否自动刷新,默认为 false。

5.4 Application

创建 Application.java 类,配置 @SpringBootApplication 注解即可。代码如下:

@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

}

5.5 简单测试

下面,我们进行下简单测试。

  • 首先,我们会使用 Jasypt 将 demo-application 进行加密,获得加密结果。
  • 然后,将加密结果,赋值到 Nacos 配置集 example-jasyptspring.application.name 配置项。
  • 最后,我们会使用 Jasypt 将 spring.application.name 配置项解密。

创建 JasyptTest 测试类,编写测试代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class JasyptTest {

@Autowired
private StringEncryptor encryptor;

@Test
public void encode() {
String applicationName = "demo-application";
System.out.println(encryptor.encrypt(applicationName));
}

@Value("${spring.application.name}")
// @NacosValue("${spring.application.name}")
private String applicationName;

@Test
public void print() {
System.out.println(applicationName);
}

}

  • 首先,执行 #encode() 方法,手动使用 Jasypt 将 demo-application 进行加密,获得加密结果。加密结果如下:
    nFVlMl4ZJ4vJLJ68X4x+a3CIerdaG0488LpZHKyoGxPoJkgemJT/nw==
  • 然后,将加密结果,赋值到 Nacos 配置集 example-jasyptspring.application.name 配置项。如下图所示:修改 Nacos 配置集

  • 最后,执行 #print() 方法,自动使用 Jasypt 将 spring.application.name 配置项解密。解密结果如下:

    demo-application

    • 成功正确解密,符合预期。

5.6 补充说明

目前测试下来,在将 Jasypt 集成进来时,Nacos 的「4. 自动配置刷新」功能,竟然失效了。

  • 具体的验证,胖友可以将 jasypt-spring-boot-starter 依赖设置成 <scope>test</scope>,并是使用 DemoController 进行测试。
  • 具体的原因,艿艿暂时没去调试与研究,有了解的胖友,麻烦告知下哟。

如果说,胖友暂时不需要自动配置刷新功能的话,可以考虑选择使用 Jasypt 集成。如果需要的话,那么就等待官方支持吧,暂时不要考虑使用 Jasypt 咧。

6. 监控端点

示例代码对应仓库:lab-44-nacos-config-demo-actuator

《芋道 Spring Boot 监控端点 Actuator 入门》中,我们学习了 Spring Boot Actuator 内置的监控端点。而 Nacos 有个 nacos-config-spring-boot-actuator 子项目,提供了 Nacos 作为 Spring Boot 配置中心时的监控端点。

下面,我们从「4. 自动刷新配置」lab-44-nacos-config-demo-auto-refresh 示例项目,复制出 lab-44-nacos-config-demo-actuator 作为本小节的示例。当然,我们还需要将 Actuator 集成到其中。

6.1 引入依赖

修改 pom.xml 文件中,额外引入 Actuator 相关依赖。

<!-- 实现对 Actuator 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 实现对 Nacos 作为配置中心的 Actuator 的自动化配置 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-actuator</artifactId>
<version>0.2.4</version>
</dependency>

6.2 配置文件

修改 application.yaml 配置文件,额外引入 Actuator 相关配置。

management:
endpoint:
# Health 端点配置项,对应 HealthProperties 配置类
health:
show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。
endpoints:
# Actuator HTTP 配置项,对应 WebEndpointProperties 配置类
web:
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。

  • 每个配置项的用途,胖友看下艿艿添加的详细注释。

配置完成后,启动 Spring Boot 应用,我们可以开始测试 Nacos 提供的监控端点了。

6.3 health 端点

health 端点,是 Spring Boot Actuator 内置的健康状态端点。而 Nacos 自定义了 HealthIndicator 实现类 NacosConfigHealthIndicator,获取应用连接 Nacos 的健康状态。

请求 actuator/health 地址,获取健康状态结果如下图:health 端点

6.4 nacos-config 端点

nacos-config 端点,是 Nacos 自定义端点 NacosConfigEndpoint,获得 Nacos 在 Spring Boot 的配置信息。

请求 actuator/nacos-config 地址,获取健康状态结果如下图:nacos-config 端点

7. 配置加载顺序

示例代码对应仓库:lab-44-nacos-config-demo-multi

《芋道 Spring Boot 配置文件入门》「9. 配置加载顺序」小节,我们了解了 Spring Boot 自带的配置加载顺序。本小节,我们来看看来自 Nacos 的配置,在其中的顺序。同时,我们将配置多个 Nacos 配置集,看看它们互相之间的加载顺序。

下面,我们来搭建一个用于测试配置加载顺序的示例。

7.1 引入依赖

pom.xml 文件中,引入相关依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-44-nacos-config-demo-multi</artifactId>

<dependencies>
<!-- 实现对 SpringMVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 实现对 Nacos 作为配置中心的自动化配置 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.4</version>
</dependency>
</dependencies>

</project>

7.2 配置文件

application.yml 中,添加 Nacos 配置,如下:

nacos:
# Nacos 配置中心的配置项,对应 NacosConfigProperties 配置类
config:
server-addr: 127.0.0.1:18848 # Nacos 服务器地址
bootstrap:
enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。
log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。
data-id: example-multi-01 # 使用的 Nacos 配置集的 dataId。
# data-ids: example-multi-02
type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。
namespace: # 使用的 Nacos 的命名空间,默认为 null。
auto-refresh: true # 是否自动刷新,默认为 false。
ext-config:
- server-addr: 127.0.0.1:18848 # Nacos 服务器地址
# data-id: example-multi-11 # 使用的 Nacos 配置集的 dataId。
data-ids: example-multi-11, example-multi-12
type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP。
namespace: # 使用的 Nacos 的命名空间,默认为 null。
auto-refresh: true # 是否自动刷新,默认为 false。
# - # 这里,可以继续添加。

  • nacos.config 配置项下,可以通过 data-iddata-ids 来设置使用的 Nacos 配置集。不过要注意,这两者只能二选一。
  • nacos.config.ext-config 配置项下,它是 Config 数组,可以配置多个配置集。实际上,Config 的属性和 nacos.config 是基本类似的,从艿艿这里给出来的示例,是不是已经可以发现啦。不过要注意,nacos.config 配置项下的优先级高于 nacos.config.ext-config

这里,Nacos 配置集 example-multi-01example-multi-11example-multi-12 需要创建下,具体的配置内容随意哈。

7.3 Application

创建 Application.java 类,配置 @SpringBootApplication 注解即可。代码如下:

@SpringBootApplication
// @NacosPropertySource(dataId = "example", type = ConfigType.YAML)
public class Application {

public static void main(String[] args) {
// 启动 Spring Boot 应用
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

// 查看 Environment
Environment environment = context.getEnvironment();
System.out.println(environment);
}

}

在代码中,我们去获取了 Spring Environment 对象,因为我们要从其中获取到 PropertySource 配置来源。DEBUG 运行 Application,并记得在 System.out.println(environment); 代码块打一个断点,可以看到如下图的调试信息:调试信息

  • 每一个 Nacos 配置集,对应一个 PropertySource 对象,并且 nacos.config 配置项下的优先级高于 nacos.config.ext-config
  • 所有 Nacos 配置集的 PropertySource 对象,排在 application.yaml 配置文件的 PropertySource 对象后面,也就是优先级最低。

7.4 补充说明

搞懂配置加载顺序的作用,很多时候是解决多个配置来源,里面配置了相同的配置项。艿艿建议的话,尽量避免出现相同配置项,排查起来还挺麻烦的。

不过所幸,在日常开发中,我们也很少会设置相同的配置项 😜。

666. 彩蛋

至此,我们已经完成了在 Spring Boot 项目中使用 Nacos 作为配置中心。不过一般使用到配置中心,公司一般已经采用微服务架构。

😜 我们目前公司还是采用 Apollo 作为配置中心,主要考虑权限模型设计的更加完善,采用的公司也较为多。Nacos 目前整体还是在迭代为主,个人意见哈~

文章目录
  1. 1. 1. 概述
  2. 2. 2. 快速入门
    1. 2.1. 2.1 引入依赖
    2. 2.2. 2.2 配置文件
    3. 2.3. 2.3 创建 Nacos 配置集
    4. 2.4. 2.4 OrderProperties
    5. 2.5. 2.5 Application
  3. 3. 3. 多环境配置
    1. 3.1. 3.1 创建 Nacos 命名空间
    2. 3.2. 3.2 创建 Nacos 配置集
    3. 3.3. 3.3 引入依赖
    4. 3.4. 3.4 配置文件
    5. 3.5. 3.5 ProfilesApplication
    6. 3.6. 3.6 简单测试
  4. 4. 4. 自动刷新配置
    1. 4.1. 4.1 引入依赖
    2. 4.2. 4.2 创建 Nacos 配置集
    3. 4.3. 4.3 配置文件
    4. 4.4. 4.4 TestProperties
    5. 4.5. 4.5 DemoController
    6. 4.6. 4.6 Application
    7. 4.7. 4.7 简单测试
    8. 4.8. 4.8 Nacos 配置监听器
    9. 4.9. 4.9 再次测试
  5. 5. 5. 配置加密
    1. 5.1. 5.1 引入依赖
    2. 5.2. 5.2 创建 Nacos 配置集
    3. 5.3. 5.3 配置文件
    4. 5.4. 5.4 Application
    5. 5.5. 5.5 简单测试
    6. 5.6. 5.6 补充说明
  6. 6. 6. 监控端点
    1. 6.1. 6.1 引入依赖
    2. 6.2. 6.2 配置文件
    3. 6.3. 6.3 health 端点
    4. 6.4. 6.4 nacos-config 端点
  7. 7. 7. 配置加载顺序
    1. 7.1. 7.1 引入依赖
    2. 7.2. 7.2 配置文件
    3. 7.3. 7.3 Application
    4. 7.4. 7.4 补充说明
  8. 8. 666. 彩蛋