从源码角度分析 Nacos 配置文件加载以及加载优先级

简介: 从源码角度分析 Nacos 配置文件加载以及加载优先级

前言

源码版本以 2021.0.1.0 版本进行分析:spring-cloud-starter-alibaba-nacos-config-2021.0.1.0

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  <version>2021.0.1.0</version>
</dependency>

核心类 NacosPropertySourceLocator 加载过程

当项目中引入了当前依赖,可以查看引入的依赖信息,关心它自动装配的类,规则加载流程主要关注这个类

// 对 @Bean 注解标注方法不通过代理调用
@Configuration(proxyBeanMethods = false)
// spring.cloud.nacos.config.enabled 默认为 true
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true)
public class NacosConfigBootstrapConfiguration {
  @Bean
  @ConditionalOnMissingBean
  public NacosConfigProperties nacosConfigProperties() {
    // spring.cloud.nacos.config 前缀的所有配置内容,都存储在这个类下面
    return new NacosConfigProperties();
  }
  @Bean
  @ConditionalOnMissingBean
  public NacosConfigManager nacosConfigManager(
      NacosConfigProperties nacosConfigProperties) {
    // 用于管理配置内容,创建 Nacos 客户端动态刷新的核心类以及设置动态刷新的监听
    return new NacosConfigManager(nacosConfigProperties);
  }
  @Bean
  public NacosPropertySourceLocator nacosPropertySourceLocator(
      NacosConfigManager nacosConfigManager) {
    // 属性配置文件解析
    return new NacosPropertySourceLocator(nacosConfigManager);
  }
}

NacosPropertySourceLocator 类是处理配置规则加载的核心类;NacosConfigProperties 类是基础配置类存储配置信息的;NacosConfigManager 是用于所有配置源的,会使用它来创建动态配置的监听以及 Nacos 客户端核心类

NacosConfigManager 依赖于 NacosConfigProperties,会优先注入

NacosPropertySourceLocator 依赖于 NacosConfigManager,会优先注入

加载配置的方法入口:NacosPropertySourceLocator#locate,首先梳理该方法是如何被调用的,Debug 断点在该方法上,启动应用,就可以看到下面调用的方法堆栈信息:

流程图梳理如下:

核心方法 NacosPropertySourceLocator#locate

在这里主要介绍的是 NacosPropertySourceLocator 类,看它是如何加载配置文件内容以及配置文件优先级处理的?

到这里,开始分析 NacosPropertySourceLocator#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;
  }
  // 设置读取配置的超时时间,默认为 3000 毫秒
  long timeout = nacosConfigProperties.getTimeout();
  nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout);
  String name = nacosConfigProperties.getName();
  // 优先获取此属性:spring.cloud.nacos.config.prefix
  String dataIdPrefix = nacosConfigProperties.getPrefix();
  if (StringUtils.isEmpty(dataIdPrefix)) {
    // 未配置取:spring.cloud.nacos.config.name
    dataIdPrefix = name;
  }
  if (StringUtils.isEmpty(dataIdPrefix)) {
    // 未配置取:spring.application.name
    dataIdPrefix = env.getProperty("spring.application.name");
  }
  CompositePropertySource composite = new CompositePropertySource(
    NACOS_PROPERTY_SOURCE_NAME);
  // 优先级在这里体现:shared-configs < ext-configs < 当前项目配置
  // 当前项目配置内也存在一个优先级:default < 扩展符配置 < 环境标识符配置
  // 在这些配置文件中设置了 refresh=true 标识,才进行动态刷新,先会追加到监听器中
  loadSharedConfiguration(composite);
  loadExtConfiguration(composite);
  loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
  // 组合所有读取到的配置文件返回,后续再 Spring 子容器中调用 refresh 方法时再填充属性阶段进行赋值.
  return composite;
}

此方法加载了在 bootstrap 中配置的文件,应用名|应用名+后缀|应用名+profile+后缀、shared-configs、extension-configs

从源码上就可以看出,最后加载的文件肯定是优先级最高的,即 loadApplicationConfiguration 方法,该方法内又区分了应用内多个配置文件下的优先级:应用名 < 应用名+后缀 < 应用名+profile+后缀

/**
 * 加载 shared-configs 配置
 */
private void loadSharedConfiguration(
  CompositePropertySource compositePropertySource) {
  List<NacosConfigProperties.Config> sharedConfigs = nacosConfigProperties
    .getSharedConfigs();
  if (!CollectionUtils.isEmpty(sharedConfigs)) {
    checkConfiguration(sharedConfigs, "shared-configs");
    loadNacosConfiguration(compositePropertySource, sharedConfigs);
  }
}
/**
 * 加载 extension-configs 配置
 */
private void loadExtConfiguration(CompositePropertySource compositePropertySource) {
  List<NacosConfigProperties.Config> extConfigs = nacosConfigProperties
    .getExtensionConfigs();
  if (!CollectionUtils.isEmpty(extConfigs)) {
    checkConfiguration(extConfigs, "extension-configs");
    loadNacosConfiguration(compositePropertySource, extConfigs);
  }
}
/**
 * 加载应用内配置
 */
private void loadApplicationConfiguration(
  CompositePropertySource compositePropertySource, String dataIdPrefix,
  NacosConfigProperties properties, Environment environment) {
  // 不设置,默认值:properties
  String fileExtension = properties.getFileExtension();
  String nacosGroup = properties.getGroup();
  // 加载默认的,不追加扩展符的 data-id 文件
  loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
                         fileExtension, true);
  // 加载带扩展符的 data-id 文件,优先级大于默认的
  loadNacosDataIfPresent(compositePropertySource,
                         dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
  // 加载带环境标识的 data-id 文件,优先级大于带扩展符的
  for (String profile : environment.getActiveProfiles()) {
    String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
    loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
                           fileExtension, true);
  }
}

加载 shared-configs、extension-configs 配置时会去检测 dataId 是否为空,为空就会出现异常

/**
 * 检查配置是否有问题,dataId 不允许为空
 */
private void checkConfiguration(List<NacosConfigProperties.Config> configs, String tips) {
  for (int i = 0; i < configs.size(); i++) {
    String dataId = configs.get(i).getDataId();
    if (dataId == null || dataId.trim().length() == 0) {
      throw new IllegalStateException(String.format(
        "the [ spring.cloud.nacos.config.%s[%s] ] must give a dataId",
        tips, i));
    }
  }
}

以上源码图是加载属性的部分,在加载属性时,若已经动态刷新过了&未开启动态刷新,就会取用之前的数据;否则,就会调用 NacosPropertySourceBuilder#build 方法

NacosPropertySource build(String dataId, String group, String fileExtension,
      boolean isRefreshable) {
   // 调用 NacosConfigService.getConfig 方法获取 dataId 对应的配置内容
   List<PropertySource<?>> propertySources = loadNacosData(dataId, group, fileExtension);
   NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources,
         group, dataId, new Date(), isRefreshable);
   // 封装完对象后存入到 NacosPropertySourceRepository#NACOS_PROPERTY_SOURCE_REPOSITORY 集合中
   NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
   return nacosPropertySource;
}

从上面源码可以看出,若开启了刷新机制,就会往 NacosPropertySourceRepository#NACOS_PROPERTY_SOURCE_REPOSITORY 集合中塞数据;若未开启的话,就不会塞数据

NacosPropertySourceRepository#NACOS_PROPERTY_SOURCE_REPOSITORY 集合里的元素是后续用来注册监听器的,涉及到动态监听的部分了,这块后续有文章对整个动态刷新机制源码的讲解

无论其是否开启动态刷新,最终都会把数据添加到 CompositePropertySource#propertySources 集合中,它里面的元素最终用处在于: Bean 生命周期-填充属性阶段

到这里,整个配置规则加载的源码分析就完成了

总结

该篇文章从源码角度上分析了 Nacos 配置文件如何加载的过程以及常见的一些会出现在细节上忽略的问题,配置文件上加载的优先级:shared-configs、extension-configs、应用配置,最后对配置文件加载的入口以及配置规则解析的源码进行了分析,包括:动态刷新监听器的触发点->NacosPropertySourceRepository#NACOS_PROPERTY_SOURCE_REPOSITORY

更多技术文章可以查看:vnjohn 个人博客

目录
相关文章
|
6月前
|
运维 监控 Java
nacos常见问题之获取配置文件的时候报错user not found如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
1746 2
|
6月前
|
运维 Java API
nacos常见问题之Nacos读取配置文件失败如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
|
6月前
|
运维 Java Nacos
nacos常见问题之读取不到配置文件如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
3913 2
|
1月前
|
Java Shell Nacos
升级Nacos竟然踩了这种坑?配置文件里的“隐形杀手”!
本文介绍了从Nacos 1.3.0升级到2.3.0的过程及注意事项,涵盖单机与集群模式的升级步骤,特别分享了一次因配置文件中多余空格导致的服务启动失败的经历,提醒读者注意配置细节。
52 0
|
3月前
|
关系型数据库 MySQL Java
“惊呆了!无需改动Nacos源码,轻松实现SGJDBC连接MySQL?这操作太秀了,速来围观,错过等哭!”
【8月更文挑战第7天】在使用Nacos进行服务治理时,常需连接MySQL存储数据。使用特定的SGJDBC驱动连接MySQL时,一般无需修改Nacos源码。需确保SGJDBC已添加至类路径,并在Nacos配置文件中指定使用SGJDBC的JDBC URL。示例中展示如何配置Nacos使用MySQL及SGJDBC,并在应用中通过Nacos API获取配置信息建立数据库连接,实现灵活集成不同JDBC驱动的目标。
113 0
|
5月前
|
SQL 关系型数据库 数据库
nacos 2.2.3版本 查看配置文件的历史版本的接口 是针对MySQL数据库的sql 改成postgresql后 sql语句报错 该怎么解决
在Nacos 2.2.3中切换到PostgreSQL后,执行配置文件历史版本分页查询出错,因`LIMIT 0, 10`语法不被PostgreSQL支持,需改为`LIMIT 10 OFFSET 0`。仅当存在历史版本时报错。解决方案是调整查询SQL以兼容PostgreSQL语法。
|
5月前
|
数据库连接 网络安全 Nacos
Nacos 的配置文件中修改了数据库的连接地址
Nacos 的配置文件中修改了数据库的连接地址
|
Java Nacos 数据库
nacos源码打包及相关配置
nacos源码打包及相关配置
314 4
|
6月前
|
前端开发 Java 网络安全
nacos常见问题之Nacos获取配置文件时报错如何解决
Nacos是阿里云开源的服务发现和配置管理平台,用于构建动态微服务应用架构;本汇总针对Nacos在实际应用中用户常遇到的问题进行了归纳和解答,旨在帮助开发者和运维人员高效解决使用Nacos时的各类疑难杂症。
|
6月前
|
缓存 Java 网络安全
Nacos报错问题之获取配置文件的时候报错如何解决
Nacos是一个开源的、易于部署的动态服务发现、配置管理和服务管理平台,旨在帮助微服务架构下的应用进行快速配置更新和服务治理;在实际运用中,用户可能会遇到各种报错,本合集将常见的Nacos报错问题进行归纳和解答,以便使用者能够快速定位和解决这些问题。