Spring Cloud Alibaba Nacos Config加载配置详解流程

作者:喜欢小苹果的码农 时间:2022-03-24 23:06:01 

1、加载节点

SpringBoot启动时,会执行这个方法:SpringApplication#run,这个方法中会调prepareContext来准备上下文,这个方法中调用了applyInitializers方法来执行实现了ApplicationContextInitializer接口的类的initialize方法。其中包括PropertySourceBootstrapConfiguration#initialize 来加载外部的配置。

@Autowired(required = false)
private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
public void initialize(ConfigurableApplicationContext applicationContext) {
...
  for (PropertySourceLocator locator : this.propertySourceLocators) {
      //载入PropertySource
     Collection<PropertySource<?>> source = locator.locateCollection(environment);
     ...
  }
  ...
}
//org.springframework.cloud.bootstrap.config.PropertySourceLocator#locateCollection
default Collection<PropertySource<?>> locateCollection(Environment environment) {
   return locateCollection(this, environment);
}
//org.springframework.cloud.bootstrap.config.PropertySourceLocator#locateCollection
static Collection<PropertySource<?>> locateCollection(PropertySourceLocator locator, Environment environment) {
   //执行locate方法,将PropertySource加载进来
   PropertySource<?> propertySource = locator.locate(environment);
   ...
}

这个类中会注入实现了PropertySourceLocator接口的类,在nacos中是NacosPropertySourceLocator。

在initialize方法中会执行NacosPropertySourceLocator的locate方法,将NacosPropertySource加载进来。

2、NacosPropertySourceLocator的注册

NacosPropertySourceLocator在配置类NacosConfigBootstrapConfiguration中注册。

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true)
public class NacosConfigBootstrapConfiguration {
  @Bean
  @ConditionalOnMissingBean
  public NacosConfigProperties nacosConfigProperties() {
     return new NacosConfigProperties();
  }
  @Bean
  @ConditionalOnMissingBean
  public NacosConfigManager nacosConfigManager(
        NacosConfigProperties nacosConfigProperties) {
     return new NacosConfigManager(nacosConfigProperties);
  }
  @Bean
  public NacosPropertySourceLocator nacosPropertySourceLocator(
        NacosConfigManager nacosConfigManager) {
     return new NacosPropertySourceLocator(nacosConfigManager);
  }
}

在这里会依次注册NacosConfigProperties,NacosConfigManager,NacosPropertySourceLocator。

3、加载

//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#locate
public PropertySource<?> locate(Environment env) {
  nacosConfigProperties.setEnvironment(env);
   //获取ConfigService
  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
  nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
        timeout);
  String name = nacosConfigProperties.getName();
//获取dataIdPrefix,一次从prefix,name,spring.application.name中获取
  String dataIdPrefix = nacosConfigProperties.getPrefix();
  if (StringUtils.isEmpty(dataIdPrefix)) {
     dataIdPrefix = name;
  }
  if (StringUtils.isEmpty(dataIdPrefix)) {
     dataIdPrefix = env.getProperty("spring.application.name");
  }
//构建CompositePropertySource:NACOS
  CompositePropertySource composite = new CompositePropertySource(
        NACOS_PROPERTY_SOURCE_NAME);
//加载share 配置
  loadSharedConfiguration(composite);
  //加载extention 配置
  loadExtConfiguration(composite);
   //加载application配置
  loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
  return composite;
}

3.1、加载share

private void loadSharedConfiguration(
     CompositePropertySource compositePropertySource) {
  //获取share节点的配置信息
  List<NacosConfigProperties.Config> sharedConfigs = nacosConfigProperties
        .getSharedConfigs();
   //如果不为空,加载
  if (!CollectionUtils.isEmpty(sharedConfigs)) {
     checkConfiguration(sharedConfigs, "shared-configs");
     loadNacosConfiguration(compositePropertySource, sharedConfigs);
  }
}

加载配置,公用方法

//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosConfiguration
private void loadNacosConfiguration(final CompositePropertySource composite,
                                   List<NacosConfigProperties.Config> configs) {
   for (NacosConfigProperties.Config config : configs) {
       loadNacosDataIfPresent(composite, config.getDataId(), config.getGroup(),
                              NacosDataParserHandler.getInstance()
                              .getFileExtension(config.getDataId()),
                              config.isRefresh());
   }
}
//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosDataIfPresent
private void loadNacosDataIfPresent(final CompositePropertySource composite,
     final String dataId, final String group, String fileExtension,
     boolean isRefreshable) {
  if (null == dataId || dataId.trim().length() < 1) {
     return;
  }
  if (null == group || group.trim().length() < 1) {
     return;
  }
   //加载NacosPropertySource,后面也会用这个方法
  NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group,
        fileExtension, isRefreshable);
   //将NacosPropertySource放入第一个
  this.addFirstPropertySource(composite, propertySource, false);
}

加载NacosPropertySource

//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosPropertySource
private NacosPropertySource loadNacosPropertySource(final String dataId,
     final String group, String fileExtension, boolean isRefreshable) {
  //标注@RefreshScope的类的数量不为0,且不允许自动刷新,从缓存加载nacos的配置
   if (NacosContextRefresher.getRefreshCount() != 0) {
     if (!isRefreshable) {
        return NacosPropertySourceRepository.getNacosPropertySource(dataId,
              group);
     }
  }
  return nacosPropertySourceBuilder.build(dataId, group, fileExtension,
        isRefreshable);
}

从缓存中加载

//NacosPropertySourceRepository
private final static ConcurrentHashMap<String, NacosPropertySource> NACOS_PROPERTY_SOURCE_REPOSITORY = new ConcurrentHashMap<>();
public static NacosPropertySource getNacosPropertySource(String dataId,
     String group) {
  return NACOS_PROPERTY_SOURCE_REPOSITORY.get(getMapKey(dataId, group));
}
public static String getMapKey(String dataId, String group) {
  return String.join(NacosConfigProperties.COMMAS, String.valueOf(dataId),
        String.valueOf(group));
}

NacosPropertySourceRepository中缓存了NacosPropertySource

获取配置并加入缓存

//com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#build
NacosPropertySource build(String dataId, String group, String fileExtension,
     boolean isRefreshable) {
   //获取配置
  List<PropertySource<?>> propertySources = loadNacosData(dataId, group,
        fileExtension);
   //包装成NacosPropertySource
  NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources,
        group, dataId, new Date(), isRefreshable);
   //加入缓存
  NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
  return nacosPropertySource;
}
//com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData
private List<PropertySource<?>> loadNacosData(String dataId, String group,
     String fileExtension) {
  String data = null;
  try {
      //获取配置
     data = configService.getConfig(dataId, group, timeout);
     if (StringUtils.isEmpty(data)) {
        log.warn(
              "Ignore the empty nacos configuration and get it based on dataId[{}] & group[{}]",
              dataId, group);
        return Collections.emptyList();
     }
     if (log.isDebugEnabled()) {
        log.debug(String.format(
              "Loading nacos data, dataId: '%s', group: '%s', data: %s", dataId,
              group, data));
     }
     return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,
           fileExtension);
  }
  catch (NacosException e) {
     log.error("get data from Nacos error,dataId:{} ", dataId, e);
  }
  catch (Exception e) {
     log.error("parse data from Nacos error,dataId:{},data:{}", dataId, data, e);
  }
  return Collections.emptyList();
}
//com.alibaba.nacos.client.config.NacosConfigService#getConfig
public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
   return getConfigInner(namespace, dataId, group, timeoutMs);
}
//com.alibaba.nacos.client.config.NacosConfigService#getConfigInner
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
   group = blank2defaultGroup(group);
   ParamUtils.checkKeyParam(dataId, group);
   //声明响应
   ConfigResponse cr = new ConfigResponse();
   //设置dataId,tenant,group
   cr.setDataId(dataId);
   cr.setTenant(tenant);
   cr.setGroup(group);
   // use local config first
   //先从本地缓存中加载
   String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant);
   if (content != null) {
       LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}",
               worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
       cr.setContent(content);
       String encryptedDataKey = LocalEncryptedDataKeyProcessor
               .getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
       cr.setEncryptedDataKey(encryptedDataKey);
       configFilterChainManager.doFilter(null, cr);
       content = cr.getContent();
       return content;
   }
   try {
       //从服务器获取配置
       ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false);
       cr.setContent(response.getContent());
       cr.setEncryptedDataKey(response.getEncryptedDataKey());
       configFilterChainManager.doFilter(null, cr);
       content = cr.getContent();
       return content;
   } catch (NacosException ioe) {
       if (NacosException.NO_RIGHT == ioe.getErrCode()) {
           throw ioe;
       }
       LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",
               worker.getAgentName(), dataId, group, tenant, ioe.toString());
   }
   LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}",
           worker.getAgentName(), dataId, group, tenant, ContentUtils.truncateContent(content));
   content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant);
   cr.setContent(content);
   String encryptedDataKey = LocalEncryptedDataKeyProcessor
           .getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
   cr.setEncryptedDataKey(encryptedDataKey);
   configFilterChainManager.doFilter(null, cr);
   content = cr.getContent();
   return content;
}

将NacosPropertySource加入composite的第一个

//com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#addFirstPropertySource
private void addFirstPropertySource(final CompositePropertySource composite,
     NacosPropertySource nacosPropertySource, boolean ignoreEmpty) {
  if (null == nacosPropertySource || null == composite) {
     return;
  }
  if (ignoreEmpty && nacosPropertySource.getSource().isEmpty()) {
     return;
  }
  composite.addFirstPropertySource(nacosPropertySource);
}

3.2、加载extention

private void loadExtConfiguration(CompositePropertySource compositePropertySource) {
   //获取extention节点的配置信息
  List<NacosConfigProperties.Config> extConfigs = nacosConfigProperties
        .getExtensionConfigs();
   //如果不为空,加载
  if (!CollectionUtils.isEmpty(extConfigs)) {
     checkConfiguration(extConfigs, "extension-configs");
     loadNacosConfiguration(compositePropertySource, extConfigs);
  }
}

3.3、加载主配置文件

private void loadApplicationConfiguration(
     CompositePropertySource compositePropertySource, String dataIdPrefix,
     NacosConfigProperties properties, Environment environment) {
  //获取文件扩展名
  String fileExtension = properties.getFileExtension();
   //获取group
  String nacosGroup = properties.getGroup();
  // load directly once by default
   //加载nacos的配置
  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;
      //加载带profile,文件格式后缀的配置,优先级高于上一个
     loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
           fileExtension, true);
  }
}

这里会加载至少三个nacos上面的配置文件,按优先级依次为application,application.yaml,application-dev.yaml。

来源:https://blog.csdn.net/xuwenjingrenca/article/details/125155368

标签:Spring,Cloud,Alibaba,Nacos,Config,加载配置
0
投稿

猜你喜欢

  • c# 使用Task实现非阻塞式的I/O操作

    2023-07-21 23:27:39
  • C#实现文件分割和合并的示例详解

    2023-05-24 21:04:03
  • java中BigDecimal和0比较的示例代码

    2022-07-05 04:58:18
  • SpringCloud hystrix断路器与局部降级全面介绍

    2023-10-28 17:29:11
  • Android中SurfaceView和view画出触摸轨迹

    2023-12-13 22:37:28
  • C# Path类---文件路径解读

    2022-05-20 05:58:47
  • Java基础之数组模拟循环队列

    2022-08-29 12:58:06
  • 解决bufferedReader.readLine()读到最后发生阻塞的问题

    2022-07-15 06:05:38
  • Mybatis实现Mapper动态代理方式详解

    2023-08-13 08:37:41
  • Android 简单实现倒计时功能

    2023-06-27 11:35:40
  • Android操作系统之内存回收策略

    2021-09-26 06:38:01
  • C#6 null 条件运算符

    2022-03-04 19:42:14
  • 超详细讲解Java异常

    2023-10-02 07:30:16
  • Spring Boot2如何构建可部署的war包

    2023-11-29 06:40:59
  • 详解Java进阶知识注解

    2023-08-12 13:11:37
  • IDEA标签tabs多行显示的设置

    2023-09-18 06:58:08
  • Java聊天室之实现一个服务器与多个客户端通信

    2021-06-03 11:34:45
  • 关于Mybatis的@param注解及多个传参

    2021-09-10 21:17:21
  • java IO流 之 输出流 OutputString()的使用

    2023-08-11 23:16:30
  • 10个Elasticsearch查询的实用技巧分享

    2022-09-21 07:32:14
  • asp之家 软件编程 m.aspxhome.com