>>>> 😜😜😜 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