随着微服务架构的流行,配置管理变得愈发关键。Nacos(Dynamic Naming and Configuration Service)作为一款开源的服务发现和配置管理平台,为微服务的构建和管理提供了强大的支持。本文将一步步深入探讨Nacos配置中心的源码,解析其关键组件和实现原理,带领读者一窥其内部工作机制。
我们要在SpringBoot中使用Nacos配置中心的功能,需要在Maven的pom.xml引入Nacos的配置中心的starter,我这里引入的是如下配置:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.1.RELAEASE</version>
</dependency>
引入以后在jar包中存在spring.factories文件,里面配置的内容如下图所示:
Springboot启动时候调用SpringApplicaton的run方法,这是程序的入口,以下的调用关系是
SpringApplicaton.run()->
SpringApplicaton.prepareEnvironment()->
该方法的逻辑主要是创建一个环境对象,然后发布一个环境已经准备好的事件,然后通知所有的监听者,其中有一个监听者是BootstrapApplicationListener,我们知道Spring Boot应用程序的启动过程可以分为两个阶段:Bootstrap阶段和Application阶段,Bootstrap主要是加载一些父配置,那么这个BootstrapApplicationListener就是处理Bootstrap阶段的类,是创建父的ConfigurableApplicationContext对象,接着调用了SpringApplication的bootstrapServiceContext方法。
SpringApplicaton.bootstrapServiceContext()
该方法的主要作用是创建一个SpringApplicationBuilder对象,然后调用run方法又创建了一个SpringApplication对象,又调用了一遍SpringApplication的run方法,但是这个run方法加载的主类不是我们写的启动类,而是BootstrapImportSelectorConfiguration这个配置类,也就是说从这个配置类开始解析Bean放入到父容器中,该类的信息如下图:
我们看到它是一个配置类,被@Configuration注解修饰,又@Improt一个BootstrapImportSelector类,由Springboot原理可知会调用该对象的selectImports方法,该方法的声明如下图:
由该截图可知,是寻找spring.factories配置文件下由key是BootstrapConfiguration开头的类,巧了,我们在引入Nacos的配置中心的starter时候正好引入了一个类是NacosConfigBootstrapConfiguration,从第一个截图也可以看到,把这个类加载到spring容器中,如果这个类还导入一些容器对象那么同样会被加载到spring容器中,那么我们当然关注这个配置类的内容,该类的截图如下:
从这个图中我们可以看到这个类又导入了三个Bean对象,分别为NacosConfigProperties、NacosConfigManager(这个类比较复杂,下节课分析它)、NacosPropertySourceLocator,下面我分别介绍NacosConfigProperties、NacosPropertySourceLocator这两个类的作用。
一、NacosConfigProperties
这个类主要是和spring.cloud.nacos.config下的配置文件进行绑定,这个类主要属性有
1.serverAddr: Nacos 服务器的地址,格式为 host:port。示例:127.0.0.1:8848。
2.namespace: 命名空间,用于隔离不同环境或不同应用的配置。默认为 public。
3.accessKey 和 secretKey: 访问 Nacos 服务器的身份验证密钥。如果启用了安全认证,需要提供。
4.sharedConfigs:用于配置共享的配置集合,示例:pring.cloud.nacos.config.shared-configs[0]=xxx .
5.extensionConfigs:用于配置扩展的配置集合,可以为配置设置一些额外的扩展属性,以满足一些特定的需求,示例:spring.cloud.nacos.config.extension-configs[0]=xxx .
6.fileExtension:用于配置文件的扩展名,默认是properties,可以改为yaml,如果改成yaml,将按照这种形式去解析配置文件
二、NacosPropertySourceLocator
NacosPropertySourceLocator主要作用是把Nacos 配置中心的属性源(Property Source)加载到spring容器中,使得可以用Nacos配置中心的配置,下面我具体分析该类作用,该类的主要方法是locate方法,该方法的代码如下:
public PropertySource<?> locate(Environment env) {
nacosConfigProperties.setEnvironment(env);
ConfigService configService = nacosConfigManager.getConfigService();
if (null == configService) {
log.warn("no instance of config service found, can't load config from nacos");
return null;
}
long timeout = nacosConfigProperties.getTimeout();
nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
timeout);
String name = nacosConfigProperties.getName();
String dataIdPrefix = nacosConfigProperties.getPrefix();
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = name;
}
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = env.getProperty("spring.application.name");
}
CompositePropertySource composite = new CompositePropertySource(
NACOS_PROPERTY_SOURCE_NAME);
loadSharedConfiguration(composite);
loadExtConfiguration(composite);
loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
return composite;
}
主要逻辑是创建一个CompositePropertySource对象,这个对象主要是保存一个集合Set<PropertySource>,节点调用loadSharedConfiguration方法,加载共享的配置文件信息,该方法的代码如下:
private void loadSharedConfiguration(
CompositePropertySource compositePropertySource) {
List<NacosConfigProperties.Config> sharedConfigs = nacosConfigProperties
.getSharedConfigs();
if (!CollectionUtils.isEmpty(sharedConfigs)) {
checkConfiguration(sharedConfigs, "shared-configs");
loadNacosConfiguration(compositePropertySource, sharedConfigs);
}
}
首先从NacosConfigProperties配置文件中取出sharedConfigs集合进行校验,如果格式有误抛出错误异常,程序终止,如果没有错误调用loadNacosConfiguration方法,该方法代码如下:
private void loadNacosConfiguration(final CompositePropertySource composite,
List<NacosConfigProperties.Config> configs) {
for (NacosConfigProperties.Config config : configs) {
String dataId = config.getDataId();
String fileExtension = dataId.substring(dataId.lastIndexOf(DOT) + 1);
loadNacosDataIfPresent(composite, dataId, config.getGroup(), fileExtension,
config.isRefresh());
}
}
循环遍历共享的sharedConfigs,取出里面的dataId它是共享文件的名称,fileExtension后缀名称,接着调用loadNacosDataIfPresent,由于这个方法调用层次太深,我主要说一下里面的逻辑是从Nacos服务器获取配置解析成NacosPropertySource对象,最后调用CompositePropertySource.addFirstPropertySource方法,该方法有必要说一下,先看代码:
public void addFirstPropertySource(PropertySource<?> propertySource) {
List<PropertySource<?>> existing = new ArrayList<>(this.propertySources);
this.propertySources.clear();
this.propertySources.add(propertySource);
this.propertySources.addAll(existing);
}
先把propertySources集合赋值给existing集合,然后清空propertySources集合,在添加解析的NacosPropertySource到propertySources集合中,再添加existing集合,可以看出这个方法是把解析的放在第一个位置上,因为propertySources是一个LinkedHashSet集合,是一个有序的集合。
loadSharedConfiguration方法执行完成以后调用loadExtConfiguration方法,该方法主要是加载一些扩展的配置同样是放到LinkedHashSet的第一个位置上,执行完loadExtConfiguration方法,开始调用loadApplicationConfiguration方法,该方法的代码如下:
private void loadApplicationConfiguration(
CompositePropertySource compositePropertySource, String dataIdPrefix,
NacosConfigProperties properties, Environment environment) {
String fileExtension = properties.getFileExtension();
String nacosGroup = properties.getGroup();
// load directly once by default
loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
fileExtension, true);
// load with suffix, which have a higher priority than the default
loadNacosDataIfPresent(compositePropertySource,
dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
// Loaded with profile, which have a higher priority than the suffix
for (String profile : environment.getActiveProfiles()) {
String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
fileExtension, true);
}
}
这段代码主要是加载自己项目的远程配置文件,首先加载是不带fileExtension后缀的配置文件,例如我们的项目名是test,那么加载远程服务器配置文件是test的,还是放到LinkedHashSet集合的第一个位置上,然后加载带后缀的配置文件,例如test.properties或者test.yaml,而且这里有个注释说load with suffix, which have a higher priority than the default,意思是带后缀的比不带后缀的优先级高,这表明如果有相同的配置带后缀的配置会覆盖不带配置的,这是因为后解析的会放到LinkedHashSet集合的第一个位置上。接着判断是否指定了Profiles如果有例如是test-dev.properties,这个配置文件中的内容会优先上面不带后缀和带后缀的。
总结:
这篇文章主要介绍了Nacos配置中心加载的原理,为了在Spring Boot中使用Nacos配置中心功能,需要引入Nacos配置中心的starter。通过分析pom.xml中的依赖和spring.factories配置,我们了解到Spring Boot在启动过程中调用了BootstrapApplicationListener。该监听器处理了Bootstrap阶段的配置加载,其中涉及了NacosConfigBootstrapConfiguration的导入。这个配置类的作用是导入NacosConfigProperties,NacosConfigManager,NacosPropertySourceLocator等组件。其中NacosConfigProperties主要用于与spring.cloud.nacos.config下的配置文件进行绑定,包括Nacos服务器地址,命名空间,身份验证密钥等。NacosPropertySourceLocator负责将Nacos配置中心的属性源加载到Spring容器中,实现了动态获取配置和动态刷新的功能。在具体分析了NacosConfigProperties和NacosPropertySourceLocator的作用后,文章对Nacos的配置加载过程进行了逐步解析。
通过解析共享配置、扩展配置和应用配置的加载,我们可以深入了解Nacos配置中心在Spring Boot应用中是如何工作的。最后,文章提到了配置文件的加载顺序和优先级,强调了带后缀的配置文件具有更高的优先级,且Profiles的配置会覆盖其他配置。
通过这些分析,希望对Nacos配置原理有个更清晰的认识,下篇文字我主要分析一下NacosConfigManager这个类的作用,好了,我是爱好编程的程序员老徐,如果喜欢我的文字请点赞关注,我们下期见。