preface

After writing part of the source code analysis article for the Nacos registry, I continue to look at the source code for the Nacos configuration center. There are two objectives:

  • Learn how to get the initial configuration from the Nacos configuration center when a Spring Boot project starts.
  • How to update the Spring Boot project when the Nacos configuration center configuration changes.

Of course practice is supreme, when understand the above two problems, also need to write a beggar version of the registry to strengthen cognition.

The beggar registry needs to implement two functions:

  • When a Spring Boot project starts, the initial configuration can be obtained from the beggar Registry.
  • The Spring Boot project can update the configuration when the beggar registry configuration changes.

Spring Boot initializer

We have learned in “In-depth Study of Spring Boot” (ii) System initializer:

Official description: The system initializer is a callback function that executes before the Spring container is refreshed.

Is registered properties, with the Spring Boot container needs to implement ApplicationContextInitializer interface.

Spring Cloud configuration center is relying on ApplicationContextInitializer to invoke the remote interfaces, access to configuration information.

Org. Springframework. Cloud. The bootstrap. Config package, there are such:

public class PropertySourceBootstrapConfiguration implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered { @Autowired(required = false) private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>(); / /... @Override public void initialize(ConfigurableApplicationContext applicationContext) { List<PropertySource<? >> composite = new ArrayList<>(); AnnotationAwareOrderComparator.sort(this.propertySourceLocators); boolean empty = true; / / from the application context, take out the environment class ConfigurableEnvironment environment = applicationContext. GetEnvironment (); For (PropertySourceLocator locator: enclosing propertySourceLocators) {/ / execution locateCollection method, obtaining specific configuration. // Note that the configuration has been loaded remotely after this method is executed. Collection<PropertySource<? >> source = locator.locateCollection(environment); / /... List<PropertySource<? >> sourceList = new ArrayList<>(); for (PropertySource<? > p : source) { if (p instanceof EnumerablePropertySource) { EnumerablePropertySource<? > enumerable = (EnumerablePropertySource<? >) p; sourceList.add(new BootstrapPropertySource<>(enumerable)); } / /... } composite.addAll(sourceList); empty = false; } if (! empty) { MutablePropertySources propertySources = environment.getPropertySources(); / /... InsertPropertySources (composite) insertPropertySources(Composite); / /... }} / /... }Copy the code

There’s also the Environment class in the Spring context.

If you feel strange, you can first read my two blogs “In-depth Learning About Spring Boot” (10) Environment and “In-depth Learning about Spring Boot” (11) Profiles.

This Class is the God Class between Spring Boot and Nacos.

PropertySourceLocator Property loader

This is an interface definition whose implementation class actually reads the properties.

public interface PropertySourceLocator { PropertySource<? > locate(Environment environment); default Collection<PropertySource<? >> locateCollection(Environment environment) { return locateCollection(this, environment); } / / note that the above PropertySourceBootstrapConfiguration # initialize, invoke this method. // Call the locate method. static Collection<PropertySource<? >> locateCollection(PropertySourceLocator locator, Environment environment) { PropertySource<? > propertySource = locator.locate(environment); / /... }}Copy the code

To view the relationship of this interface:

Coincidentally, there is a nacOS-related implementation. Now you can string it together.

When the Spring Boot project starts, it executesPropertySourceBootstrapConfiguration # initialize()In whichPropertySourceLocatorThe implementation class traverses the executionlocate()Methods.

Then we can have a guess, in the locate NacosPropertySourceLocator (), request configuration was obtained to the configuration, set into the Environment, Spring the Boot is not can normal Boot.

NacosPropertySourceLocator

public class NacosPropertySourceLocator implements PropertySourceLocator { @Override public PropertySource<? > locate(Environment env) { // .... CompositePropertySource composite = new CompositePropertySource( NACOS_PROPERTY_SOURCE_NAME); Load the share configuration loadSharedConfiguration(Composite). // 2. Load the extended configuration loadExtConfiguration(Composite); / / 3. Load application configuration loadApplicationConfiguration (composite, dataIdPrefix nacosConfigProperties, env); return composite; } // loadNacosDataIfPresent private void loadNacosDataIfPresent(Final CompositePropertySource Composite, final String dataId, final String group, String fileExtension, boolean isRefreshable) { // .... NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group, fileExtension, isRefreshable); this.addFirstPropertySource(composite, propertySource, false); } // continue to call loadNacosPropertySource() private NacosPropertySource loadNacosPropertySource(final String dataId, Final String Group, String fileExtension, Boolean isRefreshable) { Directly from the local cache if (NacosContextRefresher getRefreshCount ()! = 0) { if (! isRefreshable) { return NacosPropertySourceRepository.getNacosPropertySource(dataId, group); }} / / here is the method of send request return nacosPropertySourceBuilder. Build (dataId, group, fileExtension isRefreshable); }}Copy the code

NacosPropertySourceBuilder

public class NacosPropertySourceBuilder { NacosPropertySource build(String dataId, String group, String fileExtension, boolean isRefreshable) { Map<String, Object> p = loadNacosData(dataId, group, fileExtension); NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId, p, new Date(), isRefreshable); NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource); return nacosPropertySource; } private Map<String, Object> loadNacosData(String dataId, String group, String fileExtension) { String data = null; Try {// get the configuration. The point. data = configService.getConfig(dataId, group, timeout); / / format configuration Map < String, Object > dataMap. = NacosDataParserHandler getInstance (). ParseNacosData (data, fileExtension); return dataMap == null ? EMPTY_MAP : dataMap; } return EMPTY_MAP; }}Copy the code

NacosConfigService

public class NacosConfigService implements ConfigService { @Override public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { return getConfigInner(namespace, dataId, group, timeoutMs); } private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException { group = blank2defaultGroup(group); ParamUtils.checkKeyParam(dataId, group); ConfigResponse cr = new ConfigResponse(); cr.setDataId(dataId); cr.setTenant(tenant); cr.setGroup(group); / /... Try {// Get configuration information remotely. Content The content of the string is the specific configuration content. 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) { // ... } / /... }}Copy the code

Once the configuration items are retrieved remotely, they can be saved to the context for application startup.

summary

How does the Nacos Configuration Center integrate with Spring Cloud?

  1. Spring Boot has an initializer mechanism for initializing properties. Spring Cloud implements an initializer and calls it in itPropertySourceLocator#locate().
  2. Nacos onePropertySourceLocatorImplementation class, and inlocate()Method to obtain specific configuration items.