Nacos配置动态刷新及多配置读取编码实践

1,204 阅读11分钟

源码地址

承接前文,本章节会继续讲解 Nacos 配置中心的相关知识点。通过前一个章节的讲解,读者应该掌握如何在应用程序中整合 Nacos 配置中心并且从 Nacos 配置中心获取配置项,本章节会通过实际的编码来实现整合 Nacos 配置中心后非常实用的功能点:配置动态刷新和多配置文件读取。

集成 Nacos 实现动态刷新配置

实现业务开关

业务开关是一种用于控制系统功能的设备,通过开启或关闭来影响系统的行为。动态刷新配置的一个非常实用的功能点就是对于相关功能点开启或关闭的控制。Nacos 可以通过可视化界面或 API 动态修改配置项的值,从而实现业务开关的开启 / 关闭。

步骤如下:

  1. 在 Nacos Server 中创建一个配置项,用于存储业务开关的状态值。

  2. 在业务代码中使用 Nacos API 读取该开关配置项的值,并在运行时判断是否启用业务逻辑。

接下来,笔者将结合实际的编码来实现对某个业务功能的开关功能。

编码及代码释义

比如项目中存在某个功能模块,但是这个功能并不会一直开放,而是在部分时间点开启,此时就可以借助 Nacos 配置中心的动态刷新机制来实现这个业务开关。

本章节将继续在 spring-cloud-alibaba-nacos-config-demo 项目的基础上开发和讲解,在 ltd.newbee.cloud.api 包中新建 ConfigTestController 类,并新增如下代码:

package ltd.newbee.cloud.api;

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.RestController;

@RestController
@RefreshScope 
public class ConfigTestController {

    
    @Value("${activitySwitch.val:false}")
    private Boolean activitySwitch;

    @GetMapping("/configTest")
    public String configTest() {
        if (activitySwitch) {
            return "活动页面渲染所需数据";
        } else {
            return "活动未开启";
        }
    }
}


首先,在代码中定义了一个布尔值变量 activitySwitch,并使用 @Value 注解读取该变量,由于此时已经集成了 Nacos 配置中心,因此会读取 Nacos 配置中心里所配置的内容。activitySwitch 变量的默认值为 false,即使无法正确通过 Nacos Server 获取到该配置项,应用程序也可以使用默认值完成启动加载。

之后,创建了一个 configTest() 方法,其业务逻辑则是根据 activitySwitch 变量的值来控制是否返回活动页面所需的数据,如果 activitySwitch 变量值为 true 则返回 “活动页面渲染所需数据”,否则返回 “活动未开启”。

另外,实现配置动态刷新的重点是 @RefreshScope 注解,在类上声明了该注解,Nacos 配置中心里的属性变动就会动态同步到当前类的变量中。如果不添加 @RefreshScope 注解,即使监听到了属性变更,变量的值也不会被刷新。

功能验证

打开浏览器并进入 Nacos 控制台页面,在 newbee-cloud-config-service-dev.properties 中增加一行配置:

# activitySwitch开关值
activitySwitch.val=false


添加完成后,点击发布按钮,效果如下图所示。

接下来,启动 nacos-config-demo 项目(需保证 Nacos Server 和 MySQL Server 已经正常运行)。如果一切正常则打开浏览器,输入如下请求地址:

http://localhost:8094/configTest

浏览器输出的内容如下:

活动未开启


接着,再次进入 Nacos 控制台页面,修改 newbee-cloud-config-service-dev.properties 配置中的 activitySwitch.val 为 true。修改完成后,点击确认发布按钮,效果如下图所示。

此时,注意观察项目的启动日志,多了一行内容,日志如下:

2023-02-18 16:11:35.890  INFO 56695 --- [1.185_17748-dev] o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [activitySwitch.val]


这行日志说明配置项 activitySwitch.val 的更改已被程序实时监听到,再次在浏览器中访问如下请求地址:

http://localhost:8094/configTest

此时,浏览器输出的内容如下:

活动页面渲染所需数据


验证通过。程序并未重新启动就能够实时读取到配置中心里的最新数据,说明动态刷新配置的功能开发完成。

配置动态刷新的好处及应用场景

动态更新配置可以给应用程序带来如下好处。

  1. 更灵活的控制: 可以在不重启应用程序的情况下动态更改配置。
  2. 更快的问题修复:可以在发现问题后立即修复配置, 而不是等到重启应用程序。
  3. 更高的可用性:可以避免因重启应用程序导致的服务中断。
  4. 更好的灵活性: 可以根据需求随时调整配置。

总结起来就是可以让开发者在不重启应用程序的前提下更新配置信息,给系统应用带来更多的灵活性。常见的应用场景有业务开关、业务规则实时更新、灰度发布,还有动态刷新数据源、动态刷新日志级别等等非常实用的场景。

不过,某些场景则需要进行二次编码,并不是说仅仅改了几行配置就能完成的,像动态刷新数据源、动态刷新日志级别就需要自行实现监听逻辑进行二次开发。

以动态刷新数据源为例,如果在 Nacos 控制台修改了数据源的地址——spring.datasource.url 配置项,应用程序也是能够实时监听到的,系统运行日志如下所示。

2023-02-19 01:47:21.117  INFO 1264 --- [1.185_17748-dev] o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [spring.datasource.url]


不过,spring.datasource.url 配置项是刷新了,此时应用程序中连接的依然是原来的数据库。也就是说仅仅修改配置项是不会使得数据源刷新生效的,没生效的原因并不是说 spring.datasource.url 配置项没更新,该配置项是更新了,应用程序中对应的变量值也更新了,如果在项目中读取的话,是能够读到最新的参数值的。但是,因为数据源对象以及相关的代理对象在项目启动期间已经生成,这里虽然读到了最新的 spring.datasource.url 配置项,但是并不会重新执行数据源对象的构建操作。看到这里读者应该明白了,spring.datasource.url 会随着 Nacos 配置中心里的更改而更新,但是数据源对象(比如 HikariDataSource 类型的 Bean)是不会随着 spring.datasource.url 配置项的更新而更新的,这是两回事儿。

如果想要动态刷新数据源或者日志级别,就不是简简单单读取到一个配置项更新能完成的了。此时就需要开发者在应用程序中做额外的编码,监听到相关配置项更新时,重新构造数据源对象或者日志工厂对象,再放入到 IOC 容器中,这样才能完成数据源对象 / 日志级别的刷新,这种操作基本属于热更新了。

数据源动态刷新这种需求其实并不多见,因此本课程就不再赘述了,如果实在有类似的需求可以看一看类似的教程。另外,不同的数据源对象刷新,编码也是不同的,比如常用的 Hikari 数据源和 Druid 数据源,这些技术教程都能够搜索到的,可以使用如下关键字进行搜索,比如 “整合 Nacos 动态刷新日志级别”、“整合 Nacos 动态刷新数据源”。

多配置文件读取

在实际的开发工作中,会存在多个微服务共用一些配置的情况,比如某几个服务共用同一份 MySQL、Redis、Elastic Search 中间件的连接配置,或者某几个服务共享同一份业务规则的配置。这种情况下就可以将部分配置分离,在 Nacos 控制台中创建多个配置文件,不需要在每个微服务中单独管理通用配置了。之前的编码演示中都只创建了一个配置文件,接下来笔者将演示如何读取 Nacos 配置中心里的多个配置文件。

extension-configs 配置项简介

Nacos 配置中心可以通过指定不同的 DataId 来读取多个配置文件,每个 DataId 对应一个配置文件,可以通过配置中心的 API 接口读取对应的配置内容,在实际编码时,则是在 bootstrap 配置文件中增加 extension-configs 配置项对 Nacos 配置中心的多个配置文件进行配置和读取。extension-configs 是一个扩展配置功能,允许用户在 Nacos 配置中心里存储额外的配置信息,并且可以通过插件的形式扩展配置的加载和解析,这样可以更方便地管理应用程序的配置信息,实际编码时增加的代码如下所示。

properties 格式配置文件的写法如下:

spring.cloud.nacos.config.extension-configs[0].dataId=xxx-config.properties
spring.cloud.nacos.config.extension-configs[0].group=xx_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true

spring.cloud.nacos.config.extension-configs[1].dataId=xxx--xxx-config.properties
spring.cloud.nacos.config.extension-configs[1].group=xx_GROUP
spring.cloud.nacos.config.extension-configs[1].refresh=true


yml 格式配置文件的写法如下:

extension-configs: 
	- dataId: xxx-config.yml 
		group: xx_GROUP 
		refresh: true 
	- dataId: xxx-xxx-config.yml 
		group: xx_GROUP 
		refresh: true


extension-configs 中配置的是一个链表的结构,每个节点都有 dataId、group 和 refresh 三个属性,分别代表了读取的配置文件名称、分组、是否需要动态刷新。

在配置中心创建多个配置文件

打开浏览器并进入 Nacos 控制台页面,新建 switch-config.properties 配置,如下图所示。

在新建配置的页面中,笔者指定了 Data ID 为 switch-config.properties、Group 为自定义分组 COMMON_GROUP、配置格式为 Properties。在 “配置内容” 输入框中,笔者将活动开关变量从 newbee-cloud-config-service-dev.properties 分离出来并放入该配置文件中。之后再创建 redis-config.properties 配置,由于只是功能演示,该配置中并未添加任何内容。

最终的配置列表如下图所示。

在代码中增加多配置读取的配置

打开项目中的 bootstrap.application 文件,新增如下内容:

spring.cloud.nacos.config.extension-configs[0].dataId=switch-config.properties
spring.cloud.nacos.config.extension-configs[0].group=COMMON_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true

spring.cloud.nacos.config.extension-configs[1].dataId=redis-config.properties
spring.cloud.nacos.config.extension-configs[1].group=COMMON_GROUP
spring.cloud.nacos.config.extension-configs[1].refresh=true


这样,除了读取 newbee-cloud-config-service-dev.properties 配置外,本项目在启动后也会额外读取 switch-config.properties 和 redis-config.properties。

功能验证

一切配置妥当之后,就可以去启动应用程序进行功能验证了。

启动 nacos-config-demo 项目(需保证 Nacos Server 和 MySQL Server 已经正常运行)。启动过程中没有报错,启动日志如下所示:

...省略部分日志
    
2023-02-19 18:25:20.977  INFO 58324 --- [           main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-newbee-cloud-config-service-dev.properties,NEWBEE_CLOUD_DEV_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-newbee-cloud-config-service.properties,NEWBEE_CLOUD_DEV_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-newbee-cloud-config-service,NEWBEE_CLOUD_DEV_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-redis-config.properties,COMMON_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-switch-config.properties,COMMON_GROUP'}]

...省略部分日志

2023-02-19 18:25:24.192  INFO 58324 --- [           main] c.a.c.n.refresh.NacosContextRefresher    : listening config: dataId=redis-config.properties, group=COMMON_GROUP
2023-02-19 18:25:24.193  INFO 58324 --- [           main] c.a.c.n.refresh.NacosContextRefresher    : listening config: dataId=newbee-cloud-config-service, group=NEWBEE_CLOUD_DEV_GROUP
2023-02-19 18:25:24.195  INFO 58324 --- [           main] c.a.c.n.refresh.NacosContextRefresher    : listening config: dataId=switch-config.properties, group=COMMON_GROUP


可以看到在项目启动过程中,会拉取 Nacos 配置中心的多个配置文件。

接下来,在 Nacos 控制台中修改 switch-config.properties 中的配置内容。修改后也能够正常监听,日志如下所示。

2023-02-19 18:27:38.530  INFO 58324 --- [1.185_17748-dev] o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [activitySwitch.val]


到这里,功能验证的结果就出来了,对于 Nacos 配置中心里的多配置文件读取功能编码完成。

总结

本章节中主要讲解整合 Nacos 配置中心后,对于多配置文件读取和配置动态刷新的开发与编码。希望各位读者能够根据笔者提供的开发步骤顺利地完成本章节的项目修改。当然,读者如果有任何问题或者想要和笔者讨论的内容,都可以在评论区留下看法,笔者会根据读者的反馈和问题继续整理和完善本章节内容。

另外,千万不要不加控制的使用动态刷新功能,也不要刻意去追求配置的灵活性,这些都是对于动态刷新功能的滥用。比如引入配置中心后为了追求所谓的灵活性而在代码中添加了过多的 “业务开关”,乍一看是灵活了很多,但是这会极大的增加维护成本,甚至后来者根本不清楚甚至看不懂之前遗留的一些 “业务开关”。一份服务代码里有几十甚至几百个所谓的 “业务开关”,代码理解起来费劲,修改起来又担心会改错,如果开发和维护变成这个样子,为了追求灵活性而做的一些事情反而给项目带来更大的掣肘,这就是典型的南辕北辙了。

原文地址 juejin.cn