Spring Cloud Alibaba Nacos Config
喜欢小苹果的码农 人气:01、加载节点
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。
加载全部内容