先来看下Nacos官方文档对此是如何实现的
通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-config 实现配置的动态变更。
通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 实现服务的注册与发现。
前提条件
需要先下载 Nacos 并启动 Nacos server。
启动配置管理服务,在Nacos配置管理页面就可以管理服务与配置了
添加依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${latest.version}</version>
</dependency>
注意:版本 2.1.x.RELEASE 对应的是 Spring Boot 2.1.x 版本。版本 2.0.x.RELEASE 对应的是 Spring Boot 2.0.x 版本,版本 1.5.x.RELEASE 对应的是 Spring Boot 1.5.x 版本,版本2022.0.0.0-RC1对应的是Spring Boot 3.0.x
在 bootstrap.properties 中配置 Nacos server 的地址和应用名
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=example
说明:之所以需要配置 spring.application.name ,是因为它是构成 Nacos 配置管理 dataId字段的一部分。
在 Nacos Spring Cloud 中,dataId 的完整格式如下:
${prefix}-${spring.profiles.active}.${file-extension}
prefix 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix来配置。
spring.profiles.active 即为当前环境对应的 profile
注意:当 spring.profiles.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension}
file-exetension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型。
通过 Spring Cloud 原生注解 @RefreshScope 实现配置自动更新:
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {
@Value("${useLocalCache:false}")
private boolean useLocalCache;
@RequestMapping("/get")
public boolean get() {
return useLocalCache;
}
}
以上是我从Nacos官方文档摘取下来的对配置管理的描述
下面通过跟踪源码分析Nacos启动初始化时是如何从Nacos配置中心获取配置属性的一个执行流程,特别是对配置文件名称命名与Nacos配置中心DataID的命名匹配过程有比较详细的描述,很多时候遇到修改配置属性后不生效,不能动态刷新的问题时,多半也是跟配置文件名称与DataID不匹配导致的
注:以下涉及源码分析对应的版本是:Nacos Spring Cloud 2022.0.0.0-RC1,Spring Boot 3.0.x
启动初始化,准备上下文
创建启动上下文:DefaultBootstrapContext
创建应用上下文:ConfigurableApplicationContext
创建配置环境:ConfigurableEnvironment
通过SpringApplication(当前的启动应用class com.mall.user.UserProviderApplication)初始化应用上下文ConfigurableApplicationContext
在SpringApplication的applyInitializers方法中ApplicationContextInitializer继续初始化
ApplicationContextInitializer是Spring-Context组中的一个函数式接口,其针对各组件提供了相应的实现,常用的实现如下:
org.springframework.boot.web.servlet.support
.ServletContextApplicationContextInitializer,基于SpringBoot的Web应用上文初始化的实现
org.springframework.boot.devtools.restart.RestartScopeInitializer,基于SpringBoot热部署的应用上下文初始化的实现
org.apache.dubbo.spring.boot.context.DubboApplicationContextInitializer,基于Dubbo应用上下文的初始化的实现
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,基于SpringCloud属性源配置的应用初始化的实现
当前项目是基于SpringCloud框架的,所以采用的是PropertySourceBootstrapConfiguration进行初始化,我们继续往下看
在applyInitializers方法中,先通过this.getInitializers().iterator()
得到相应的应用上下文初始化实例,这里对应实例就是PropertySourceBootstrapConfiguration,接着循环通过相应的实例进行下一步初始化
进入到PropertySourceBootstrapConfiguration的initialize方法,在该方法中PropertySourceBootstrapConfiguration持有一个属性源定位器propertySourceLocators,通过该定位器去获取Nacos配置属性,然后继续往下看,中间进过几个层级的调用后进入到PropertySource<?> locate(Environment env)方法
在该方法中,先通过nacosConfigManager得到configService实例,nacosConfigManager(Nacos配置管理)其实也很简单,里面就几行代码,提供了对configService的创建
configService配置服务则是具体的去远程请求Nacos配置中心获取配置,configService在后续流程会讲到,我们接着当前方法继续看
this.nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout);
接着将configService交给NacosPropertySourceBuilder实例,以便后续通过该实例去获取配置并构建配置属性
接下来几行代码就是组织Nacos配置中心对应配置文件的Data ID的名称的前缀,通过this.nacosConfigProperties获取配置文件属性来拼接,nacosConfigProperties是配置文件中对应Nacos的属性实体类,通过Spring自动装载并初始化,实体信息如下:
继续看Data ID前缀拼接规则:
1、优先匹配是否有在bootstrap.yml配置文件中设置Nacos配置的前缀prefix
即:sping.cloud.nacos.config.prefix
- 如果没置prefix,则匹配Nacos配置的name
即:sping.cloud.nacos.config.name
- 如果以上两者都没设置,则匹配当前应用名称
即:spring.application.name
继续进入到下一个方法loadApplicationConfiguration
看看该方法中的处理逻辑:
1、先调用loadNacosDataIfPresent方法通过参数dataIdPrefix
、nacosGroup两个参数去加载配置属性,dataIdPrefix参数就是前面传过来的dataId前缀,nacosGroup则是Nacos配置中心设置的分组
- 调用loadNacosDataIfPresent方法,参数为:dataIdPrefix,fileExtension配置文件后缀,nacosGroup
- 调用loadNacosDataIfPresent方法dataIdPrefix、profile、fileExtension、nacosGroup,其中profile为对应的不同环境:dev、test、prod
接在在后续的操作中调用this.nacosPropertySourceBuilder.build(dataId, group, fileExtension, isRefreshable)方法具体构建配置属性,在this.nacosPropertySourceBuilder.build方法中会调用loadNacosData(String dataId, String group, String fileExtension)方法
在该方法中先this.configService.getConfig(dataId, group, this.timeout)方法去远程请求Nacos配置中心获取配置属性,接着调用NacosDataParserHandler.getInstance().parseNacosData(dataId, data, fileExtension)方法对获取到的属性进行解析
再进入到configService.getConfig,该方法中继续调用getConfigInner
方法
在该方法中先是通过LocalConfigInfoProcessor.getFailover(this.worker.getAgentName(), dataId, group, tenant)方发去本地磁盘去获取配置属性
继续看getConfigInner方法后面的逻辑,如果getFailover方法没有在本地获取到配置属性,则调用this.worker.getServerConfig(dataId, group, tenant, timeoutMs, false)方法发送请求去Nacos配置中心拉取配置属性,到这里Nacos启动初始化从配置中心拉取配置属性的过程就有一个清晰的流程了