四时宝库

程序员的知识宝库

SpringBoot整合Nacos配置中心加载原理一

随着微服务架构的流行,配置管理变得愈发关键。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这个类的作用,好了,我是爱好编程的程序员老徐,如果喜欢我的文字请点赞关注,我们下期见。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接