5.3 使用 Nacos Config 监听实现 Bean 属性动态刷新
前文曾提及 com.alibaba.nacos.api.config.listener.Listener
是 Nacos Client API 标准的配置监听器接口,由于仅监听配置内容,并不能直接与 Spring 体系打通,因此,需要借助于 Spring Cloud Alibaba Nacos Config API NacosConfigManager
(感谢小伙伴 liaochuntao 和 zkzlx 的代码贡献),代码调整如下:
@SpringBootApplication @RestController @RefreshScope @EnableConfigurationProperties(User.class) public class NacosConfigSampleApplication { @Value("${user.name}") private String userName; @Value("${user.age}") private int userAge; @Autowired private User user; @Autowired private NacosConfigManager nacosConfigManager; @Bean public ApplicationRunner runner() { return args -> { String dataId = "nacos-config-sample.properties"; String group = "DEFAULT_GROUP"; nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() { @Override public void receiveConfigInfo(String configInfo) { System.out.println("[Listener] " + configInfo); } }); }; } @PostConstruct public void init() { System.out.printf("[init] user name : %s , age : %d%n", userName, userAge); } @PreDestroy public void destroy() { System.out.printf("[destroy] user name : %s , age : %d%n", userName, userAge); } @RequestMapping("/user") public String user() { return "[HTTP] " + user; } public static void main(String[] args) { SpringApplication.run(NacosConfigSampleApplication.class, args); } }
代码主要变化:
@Autowired
依赖注入NacosConfigManager
- 新增
runner()
方法,通过NacosConfigManager
Bean 获取ConfigService
,并增加了AbstractListener
(Listener
抽象类)实现,监听 dataId = “nacos-config-sample.properties” 和 group = “DEFAULT_GROUP” 的配置内容
重启应用,并将配置 user.age 从 19 调整到 90,观察日志变化:
c.a.n.client.config.impl.ClientWorker : [fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=b0f42fac52934faf69757c2b6770d39c, content=user.name=nacos-config-sample user.age=90, type=properties [Listener] user.name=nacos-config-sample user.age=90 ......
在第 1 行日志下方,新增了监听实现代码的输出内容,不过这段内容是完整的配置,而非变化的内容。读者请务必注意其中的差异。下一步要解决的是将配置映射到 Bean 属性,此处给出一个简单的解决方案,实现步骤有两个:
- 将 String 内容转化为 Properties 对象
- 将 Properties 属性值设置到对应的 Bean 属性
代码调整如下:
@SpringBootApplication @RestController @RefreshScope @EnableConfigurationProperties(User.class) public class NacosConfigSampleApplication { ...... @Bean public ApplicationRunner runner() { return args -> { String dataId = "nacos-config-sample.properties"; String group = "DEFAULT_GROUP"; nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() { @Override public void receiveConfigInfo(String configInfo) { System.out.println("[Listener] " + configInfo); System.out.println("[Before User] " + user); Properties properties = new Properties(); try { properties.load(new StringReader(configInfo)); String name = properties.getProperty("user.name"); int age = Integer.valueOf(properties.getProperty("user.age")); user.setName(name); user.setAge(age); } catch (IOException e) { e.printStackTrace(); } System.out.println("[After User] " + user); } }); }; } ...... }
重启应用,并将配置 user.age 从 90 调整到 19,观察日志变化:
[Listener] user.name=nacos-config-sample user.age= 19 [Before User] User{name='nacos-config-sample', age=90} [After User] User{name='nacos-config-sample', age=19}
上述三个例子均围绕着 Nacos Config 实现 Bean 属性动态更新,不过它们是 Spring Cloud 使用场景。如果读者的应用仅使用 Spring 或者 Spring Boot,可以考虑 Nacos Spring 工程, Github 地址:https://github.com/nacos-group/nacos-spring-project,其中 @NacosValue
支持属性粒度的更新。
6. Nacos Config 高级配置
6.1 支持自定义 namespace 的配置
首先看一下 Nacos 的 Namespace 的概念, Nacos 概念
用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
在没有明确指定 ${spring.cloud.nacos.config.namespace}
配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。如果需要使用自定义的命名空间,可以通过以下配置来实现:
spring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7
注:该配置必须放在 bootstrap.properties 文件中。此外 spring.cloud.nacos.config.namespace
的值是 namespace 对应的 id,id 值可以在 Nacos 的控制台获取。并且在添加配置时注意不要选择其他的 namespae,否则将会导致读取不到正确的配置
6.2 支持自定义 Group 的配置
在没有明确指定 ${spring.cloud.nacos.config.group}
配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group,可以通过以下配置来实现:
spring.cloud.nacos.config.group=DEVELOP_GROUP
注:该配置必须放在 bootstrap.properties 文件中。并且在添加配置时 Group 的值一定要和 spring.cloud.nacos.config.group
的配置值一致。
6.3 支持自定义扩展的 Data Id 配置
Spring Cloud Alibaba Nacos Config 从 0.2.1 版本后,可支持自定义 Data Id 的配置。关于这部分详细的设计可参考 这里。一个完整的配置案例如下所示:
spring.application.name=opensource-service-provider spring.cloud.nacos.config.server-addr=127.0.0.1:8848 # config external configuration # 1、Data Id 在默认的组 DEFAULT_GROUP,不支持配置的动态刷新 spring.cloud.nacos.config.extension-configs[0].data-id=ext-config-common01.properties # 2、Data Id 不在默认的组,不支持动态刷新 spring.cloud.nacos.config.extension-configs[1].data-id=ext-config-common02.properties spring.cloud.nacos.config.extension-configs[1].group=GLOBALE_GROUP # 3、Data Id 既不在默认的组,也支持动态刷新 spring.cloud.nacos.config.extension-configs[2].data-id=ext-config-common03.properties spring.cloud.nacos.config.extension-configs[2].group=REFRESH_GROUP spring.cloud.nacos.config.extension-configs[2].refresh=true
可以看到:
- 通过
spring.cloud.nacos.config.extension-configs[n].data-id
的配置方式来支持多个 Data Id 的配置。 - 通过
spring.cloud.nacos.config.extension-configs[n].group
的配置方式自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。 - 通过
spring.cloud.nacos.config.extension-configs[n].refresh
的配置方式来控制该 Data Id 在配置变更时,是否支持应用中可动态刷新, 感知到最新的配置值。默认是不支持的。
注:多个 Data Id 同时配置时,他的优先级关系是
spring.cloud.nacos.config.extension-configs[n].data-id
其中 n 的值越大,优先级越高。
spring.cloud.nacos.config.extension-configs[n].data-id
的值必须带文件扩展名,文件扩展名既可支持 properties,又可以支持 yaml/yml。此时spring.cloud.nacos.config.file-extension
的配置对自定义扩展配置的 Data Id 文件扩展名没有影响。
通过自定义扩展的 Data Id 配置,既可以解决多个应用间配置共享的问题,又可以支持一个应用有多个配置文件。
为了更加清晰的在多个应用间配置共享的 Data Id ,你可以通过以下的方式来配置:
# 配置支持共享的 Data Id spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml # 配置 Data Id 所在分组,缺省默认 DEFAULT_GROUP spring.cloud.nacos.config.shared-configs[0].group=GROUP_APP1 # 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false spring.cloud.nacos.config.shared-configs[0].refresh=true
可以看到:
- 通过
spring.cloud.nacos.config.shared-configs[n].data-id
来支持多个共享 Data Id 的配置。 - 通过
spring.cloud.nacos.config.shared-configs[n].group
来配置自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。 - 通过
spring.cloud.nacos.config.shared-configs[n].refresh
来控制该Data Id在配置变更时,是否支持应用中动态刷新,默认false。
6.4 配置的优先级
Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。
- A: 通过
spring.cloud.nacos.config.shared-configs[n].data-id
支持多个共享 Data Id 的配置 - B: 通过
spring.cloud.nacos.config.extension-configs[n].data-id
的方式支持多个扩展 Data Id 的配置 - C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置
当三种方式共同使用时,他们的一个优先级关系是:A < B < C
6.5 完全关闭配置
通过设置 spring.cloud.nacos.config.enabled = false 来完全关闭 Spring Cloud Nacos Config
6.6 更多高级配置
更多关于 Nacos Config Starter 的配置项如下所示:
配置项 | Key | 默认值 | 说明 |
服务端地址 | spring.cloud.nacos.config.server-addr |
Nacos Server 启动监听的ip地址和端口 | |
配置对应的 DataId | spring.cloud.nacos.config.name |
先取 prefix,再取 name,最后取 spring.application.name | |
配置对应的 DataId | spring.cloud.nacos.config.prefix |
先取 prefix,再取 name,最后取 spring.application.name | |
配置内容编码 | spring.cloud.nacos.config.encode |
读取的配置内容对应的编码 | |
GROUP | spring.cloud.nacos.config.group |
DEFAULT_GROUP |
配置对应的组 |
文件扩展名 | spring.cloud.nacos.config.fileExtension |
properties |
配置项对应的文件扩展名,目前支持 properties 和 yaml(yml) |
获取配置超时时间 | spring.cloud.nacos.config.timeout |
3000 |
客户端获取配置的超时时间(毫秒) |
接入点 | spring.cloud.nacos.config.endpoint |
地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址 | |
命名空间 | spring.cloud.nacos.config.namespace |
常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等 | |
AccessKey | spring.cloud.nacos.config.accessKey |
当要上阿里云时,阿里云上面的一个云账号名 | |
SecretKey | spring.cloud.nacos.config.secretKey |
当要上阿里云时,阿里云上面的一个云账号密码 | |
Nacos Server 对应的 context path | spring.cloud.nacos.config.contextPath |
Nacos Server 对外暴露的 context path | |
集群 | spring.cloud.nacos.config.clusterName |
配置成Nacos集群名称 | |
共享配置 | spring.cloud.nacos.config.sharedDataids |
共享配置的 DataId, “,” 分割 | |
共享配置动态刷新 | spring.cloud.nacos.config.refreshableDataids |
共享配置中需要动态刷新的 DataId, “,” 分割 | |
自定义 Data Id 配置 | spring.cloud.nacos.config.extConfig |
属性是个集合,内部由 Config POJO 组成。Config 有 3 个属性,分别是 dataId , group 以及 refresh |
7. Nacos Config Actuator Endpoint
Nacos Config 内部提供了一个 Endpoint, 对应的 Endpoint ID 为 nacos-config
,其 Actuator Web Endpoint URI 为 /actuator/nacos-config
注:使用 Nacos Config Spring Cloud 1.x 版本的话,其 URI 地址则为
/nacos-config
其中,Endpoint 暴露的 json 中包含了三种属性:
- NacosConfigProperties: 当前应用 Nacos 的基础配置信息
- RefreshHistory: 配置刷新的历史记录
- Sources: 当前应用配置的数据信息
由于 Aliyun Java Initializr 所生成的应用工程默认激活 Spring Boot Actuator Endpoints(JMX 和 Web),具体配置存放在 application.properties
文件中,同时,Actuator Web 端口设置为 8081,内容如下:
management.endpoints.jmx.exposure.include=* management.endpoints.web.exposure.include=* management.endpoint.health.show-details=always # Actuator Web 访问端口 management.server.port=8081
因此,应用 nacos-config-sample 无需调整,直接访问:http://127.0.0.1:8081/actuator/nacos-config,服务响应的内容如下:
{ "NacosConfigProperties": { "serverAddr": "127.0.0.1:8848", "username": "", "password": "", "encode": null, "group": "DEFAULT_GROUP", "prefix": null, "fileExtension": "properties", "timeout": 3000, "maxRetry": null, "configLongPollTimeout": null, "configRetryTime": null, "enableRemoteSyncConfig": false, "endpoint": null, "namespace": null, "accessKey": null, "secretKey": null, "contextPath": null, "clusterName": null, "name": null, "sharedConfigs": null, "extensionConfigs": null, "refreshEnabled": true, "sharedDataids": null, "refreshableDataids": null, "extConfig": null, "configServiceProperties": { "secretKey": "", "namespace": "", "username": "", "enableRemoteSyncConfig": "false", "configLongPollTimeout": "", "configRetryTime": "", "encode": "", "serverAddr": "127.0.0.1:8848", "maxRetry": "", "clusterName": "", "password": "", "accessKey": "", "endpoint": "" } }, "RefreshHistory": [ ], "Sources": [ { "lastSynced": "2020-09-14 11:11:37", "dataId": "nacos-config-sample.properties" }, { "lastSynced": "2020-09-14 11:11:37", "dataId": "nacos-config-sample" } ] }