>>>> 😜😜😜 Github: 👉 github.com/black-ant CASE Backup: 👉 gitee.com/antblack/ca…

A. The preface

This article takes a look at the Nacos Client configuration request process and related configuration

2. Configure classes and usage

2.1 Basic Use cases

# This is also the most common case template
spring:
  application:
    name: nacos-multi-config
  cloud:
    nacos:
      config:
        extension-configs:
            # corresponds to Nacos DataId
          - data-id: nacos-multi-config-A.yaml
            group: DEFAULT_RSM
          - data-id: nacos-multi-config-B.yaml
            group: DEFAULT_RSM
          - data-id: nacos-multi-config-C.yaml
            group: DEFAULT_RSM
        # file suffix
        file-extension: yaml
        server-addr: 127.0. 01.: 8848
      discovery:
        server-addr: 127.0. 01.: 8848
Copy the code

2.2 Configuring Class Resolution

The corresponding configuration class of Nacos is NacosConfigProperties. Here are all the parameters:

/ / Nacos address
private String serverAddr;

// User name and password
private String username;
private String password;

// Content encoding
private String encode;

/ / group they belong to
private String group = "DEFAULT_GROUP";

// Config DataId prefix
private String prefix;

// Config file name extension
private String fileExtension = "properties";

// Obtain the configuration timeout period
private int timeout = 3000;

// Maximum number of reconnections
private String maxRetry;

// Get the timeout period for configuring long polling
private String configLongPollTimeout;

// Set the retry times for obtaining errors
private String configRetryTime;

// Automatically obtain and register the Listener Listener, which incurs network overhead
private boolean enableRemoteSyncConfig = false;

// Service domain name, can dynamically obtain the server address
private String endpoint;

// Namespace, separate configurations for different environments
private String namespace;

// Access keys and access keys
private String accessKey;
private String secretKey;

// Container address
private String contextPath;

/ / shard
private String clusterName;

// Naco configures the dataId name
private String name;

/ * * * * spring Shared configuration set in cloud. Nacos. Config. The Shared - configs [0] = XXX. * /
private List<Config> sharedConfigs;

Extended configuration / * * * * spring in cloud. Nacos. Config. The extension - configs [0] = XXX. * /
private List<Config> extensionConfigs;

// Whether to refresh the configuration
private boolean refreshEnabled = true;
Copy the code

3. Configuration process

Let’s take a look at the load and fetch flow of the configuration

3.1 Opening the Configuration Portal

Configuration of open is based on PropertySourceBootstrapConfiguration open,

public void initialize(ConfigurableApplicationContext applicationContext) { List<PropertySource<? >> composite =new ArrayList<>();
   AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
   boolean empty = true;
   ConfigurableEnvironment environment = applicationContext.getEnvironment();
   
   // Get resources from different Locators
   for (PropertySourceLocator locator : this.propertySourceLocators) {
       
      / / for Nacos NacosPropertySourceLocator will call hereCollection<PropertySource<? >> source = locator.locateCollection(environment);if (source == null || source.size() == 0) {
         continue; } List<PropertySource<? >> sourceList =new ArrayList<>();
      for(PropertySource<? > p : source) {/ / generated BootstrapPropertySource and SimpleBootstrapPropertySource respectively
         if (p instanceofEnumerablePropertySource) { EnumerablePropertySource<? > enumerable = (EnumerablePropertySource<? >) p; sourceList.add(new BootstrapPropertySource<>(enumerable));
         }
         else {
            sourceList.add(new SimpleBootstrapPropertySource(p));
         }
      }
      composite.addAll(sourceList);
      empty = false;
   }
   
    // Omit the processing of the resource
}
Copy the code

3.2 Nacos loading entry

During this process, the interface class PropertySourceLocator initiates the corresponding

// C- NacosPropertySourceLocator
publicPropertySource<? > locate(Environment env) { nacosConfigProperties.setEnvironment(env);// ConfigService is the main query
   ConfigService configService = nacosConfigManager.getConfigService();

   if (null == configService) {
      log.warn("no instance of config service found, can't load config from nacos");
      return null;
   }
   
   // The timeout period can be configured in the configuration file
   long timeout = nacosConfigProperties.getTimeout();
   
   // is a local variable of the class, meaning that the variable is used in general
   nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,timeout);
         
   // Prepare the prefix and name
   String name = nacosConfigProperties.getName();
   String dataIdPrefix = nacosConfigProperties.getPrefix();
   if (StringUtils.isEmpty(dataIdPrefix)) {
      dataIdPrefix = name;
   }
    
   // Notice the default prefix
   if (StringUtils.isEmpty(dataIdPrefix)) {
      dataIdPrefix = env.getProperty("spring.application.name");
   }

   CompositePropertySource composite = new CompositePropertySource(
         NACOS_PROPERTY_SOURCE_NAME);
    
   // private List
      
        sharedConfigs
      
   loadSharedConfiguration(composite);
   
   // private List
      
        extensionConfigs processing
      
   loadExtConfiguration(composite);
   
   // Obtain the master configuration file
   loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
   return composite;
}
Copy the code

Addendum 1: loadSharedConfiguration handles shared data

Configuration with multiple shared Data ids, priority less than extension-Configs, suitable for shared profiles in the same Group as the project default profile (PS: only in one Group)

The main process is to obtain the configuration, verify the accuracy, and invoke loadNacosConfiguration to initiate the call of the configuration

PS: The main process is supplement 2, which is why the priority is not as high as loadExtConfiguration

Supplement 2: loadExtConfiguration handles configuration

Main processes for nacosConfigProperties. GetExtensionConfigs (), determine whether exists, there will first checkConfiguration, Then call loadNacosConfiguration for the main process

private void loadNacosConfiguration(final CompositePropertySource composite,List<NacosConfigProperties.Config> configs) {

   // This is called in a loop, so what comes after actually overwrites what comes before
   for (NacosConfigProperties.Config config : configs) {
      // getFileExtension is the file suffixloadNacosDataIfPresent(composite, config.getDataId(), config.getGroup(), NacosDataParserHandler.getInstance().getFileExtension(config.getDataId()), config.isRefresh()); }}/ / the following logic is invoked NacosPropertySourceBuilder is a query and analysis

NacosPropertySource build(String dataId, String group, String fileExtension,boolean isRefreshable) {
   // Step 1: Query the object and complete the parsing -> 3.3List<PropertySource<? >> propertySources = loadNacosData(dataId, group,fileExtension);// Step 2: Encapsulate the parsed object as NacosPropertySource
   NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources,
         group, dataId, new Date(), isRefreshable);
   
   / / Step 3: a ConcurrentHashMap in NacosPropertySourceRepository < String, NacosPropertySource >
   NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
   return nacosPropertySource;
}
Copy the code

Supplement 3: Obtaining the master configuration file

This process has the highest priority and is used to obtain the configuration in a normal way

private void loadApplicationConfiguration( CompositePropertySource compositePropertySource, String dataIdPrefix, NacosConfigProperties properties, Environment environment) {
      
   // Get file suffixes and groups
   String fileExtension = properties.getFileExtension();
   String nacosGroup = properties.getGroup();
   
   // First load: loads directly once by default
   loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
         fileExtension, true);
         
   // Second load: Load with suffix, with higher priority than default
   loadNacosDataIfPresent(compositePropertySource,
         dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
         
   // Third load: load with the Profile file, which has a higher priority than the suffix
   // Here we loop through all Profiles to process them separately
   for (String profile : environment.getActiveProfiles()) {
      String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
      loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
            fileExtension, true); }}Copy the code

As you can see, this is loaded multiple times and prioritized in this way

3.3 Nacos Main configuration process

privateList<PropertySource<? >> loadNacosData(String dataId, String group,String fileExtension) { String data =null;

   // Step 1: Obtain remote configuration information
   data = configService.getConfig(dataId, group, timeout);
   if (StringUtils.isEmpty(data)) {
      return Collections.emptyList();
   }
   
   // Step 2: Parse the remote configuration information
   return NacosDataParserHandler.getInstance().parseNacosData(dataId, data,fileExtension);

   // The exception handling logic is omitted here. An exception is not thrown, but an empty collection is returned
   // PS: It is difficult to determine the cause from the log
   // return Collections.emptyList();
}
Copy the code

PS: This is not just a string, but a yamL data copied into the text

3.3.1 Obtaining remote Configuration

private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
    group = null2defaultGroup(group);
    ParamUtils.checkKeyParam(dataId, group);
    ConfigResponse cr = new ConfigResponse();
    
    // Prepare the query object
    // {dataId=nacos-multi-config-B.yaml, tenant=, group=DEFAULT_RSM}
    cr.setDataId(dataId);
    cr.setTenant(tenant);
    cr.setGroup(group);
    
    // Local configuration is preferred
    String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);
    // If there is a local configuration, this parameter is directly returned
    
    try {
        // Initiate a remote call
        // HttpAgent.httpGet(Constants.CONFIG_CONTROLLER_PATH, null, params, agent.getEncode(), readTimeout)
        String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
        cr.setContent(ct[0]);
        
        configFilterChainManager.doFilter(null, cr);
        content = cr.getContent();
        
        return content;
    } catch (NacosException ioe) {
        if (NacosException.NO_RIGHT == ioe.getErrCode()) {
            throwioe; }}// If it is not 403, it will be obtained by snapshot
    content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);
    cr.setContent(content);
    configFilterChainManager.doFilter(null, cr);
    content = cr.getContent();
    return content;
}
Copy the code

3.3.2 Remote Configuration Resolution

// C- NacosDataParserHandler
publicList<PropertySource<? >> parseNacosData(String configName, String configValue, String extension)throws IOException {
      
     
   if (StringUtils.isEmpty(configValue)) {
      return Collections.emptyList();
   }
   
   // File suffix
   if (StringUtils.isEmpty(extension)) {
      extension = this.getFileExtension(configName);
   }
   
   // There are four types of PropertySourceLoader:
   / / - NacosXmlPropertySourceLoader: XML file format
   / / - PropertiesPropertySourceLoader: a Properties file format
   // -yamlpropertysourceloader: YAML file format
   / / - NacosJsonPropertySourceLoader: JSON file format
   for (PropertySourceLoader propertySourceLoader : propertySourceLoaders) {
   
      if(! canLoadFileExtension(propertySourceLoader, extension)) {// If it cannot be loaded, exit
         continue;
      }
      NacosByteArrayResource nacosByteArrayResource;
      // omit conversion to Byte logic: new NacosByteArrayResource
      
      nacosByteArrayResource.setFilename(getFileName(configName, extension));
      
      / / core logic, transform yaml formats text to OriginTrackedMapPropertySource hereList<PropertySource<? >> propertySourceList = propertySourceLoader .load(configName, nacosByteArrayResource);if (CollectionUtils.isEmpty(propertySourceList)) {
         return Collections.emptyList();
      }
      
      return propertySourceList.stream().filter(Objects::nonNull)
            .map(propertySource -> {
               if (propertySource instanceof EnumerablePropertySource) {
                  // Get the Name
                  String[] propertyNames = ((EnumerablePropertySource) propertySource)
                        .getPropertyNames();
                  if(propertyNames ! =null && propertyNames.length > 0) {
                     Map<String, Object> map = new LinkedHashMap<>();
                     
                     // OriginTrackedValue is converted to a value of the corresponding type
                     Arrays.stream(propertyNames).forEach(name -> {
                        map.put(name, propertySource.getProperty(name));
                     });
                     
                     // Finally build a List collection of PropertySource
                     return new OriginTrackedMapPropertySource(
                           propertySource.getName(), map, true); }}return propertySource;
            }).collect(Collectors.toList());
   }
   return Collections.emptyList();
}

Copy the code

Added: YamlPropertySourceLoader process

publicList<PropertySource<? >> load(String name, Resource resource)throws IOException {

   OriginTrackedYamlLoader: OriginTrackedYamlLoader: OriginTrackedYamlLoader: OriginTrackedYamlLoader
   List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
   
   / / ellipsis sentence empty and add logic - > propertySources. Add (new OriginTrackedMapPropertySource
   return propertySources;
}
Copy the code

4. Summarize

Space is limited, so the coverage and integration of local configurations, as well as the sequential loading and prioritization of configurations are to be sorted out in the next document. A flow chart is shared below

Added: Indicates the configured priority

  • Application Main Configuration > extensionConfigs > sharedConfigs
  • ExtensionConfigs/sharedConfigs row in the back of the array than the previous high priority
  • Application main logic Profile > suffix