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


猜你喜欢
- 前言面对众多卡片层叠效果,我们的产品童鞋也突发奇想,搞出了另一种卡片层叠切换展示的交互,而且产品狗们居然要求多做几种动效给他们看,好让他们选
- 常量池Java中我们创建String对象有两种基本方法。String str1 = "zxhtom";String st
- 一、layui.use1、LayUI的官方使用文档:https://www.layui.com/doc/2、layui的内置模块不是默认就加
- Android中子线程和UI线程之间通信的详细解释 1.在多线程编程这块,我们经常要使用Handler,Thread和Runnable这三个
- 说明:1、集合类型参数化;2、可根据集合中的对象的各个属性进行排序,传入属性名称即可;注:属性必须实现了IComparable接口,C#中i
- 随着C#的发展,该语言内容不断丰富,开发变得更加方便快捷,C# 的锋利尽显无疑。C# 语言从诞生起就是强类型语言,这一性质到今天不曾改变,我
- 引言MyBatis-Plus | 最优雅最简洁地完成数据库操作是对MyBatis-Plus的功能进行简单介绍,虽然是介绍,也让我们领略到他的
- 桥接模式桥接模式是将抽象部分与它的实现部分分离,使他们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)
- 前言:先写个简单的地点签到功能,如果日后有时间细写的话,会更加好好研究一下百度地图api,做更多逻辑判断。这里主要是调用百度地图中的场景定位
- 在很多场景中我们需要验证时间日期的是否属于正确的格式,验证时间是否符合常规的。1、验证 yyyy-MM-dd HH:mm:dd 格式的日期S
- Android InputAndroid Input指的是输入事件,主要是触摸滑动,当然还包括类似蓝牙外设的输入。Input涉及到的主要模块
- 一、搭建步骤1、导入jar包、创建项目包结构2、在web.xml中配置前端控制器3、编写springMvc核心配置文件4、编写pojo类和C
- 会话是识别用户,跟踪用户访问行为的一个手段,通过cookie(存在客户端)或session(存在服务端)来判断本次请求是那个客户端发送过来;
- 在使用手机时,蓝牙通信给我们带来很多方便。那么在Android手机中怎样进行蓝牙开发呢?本文以实例的方式讲解Android蓝牙开发的知识。&
- 枚举(Enum)定义enum关键字用于声明枚举,即一种由一组称为枚举数列表的命名常量组成的独特类型。通常情况下,最好是在命名空间内直接定义枚
- 近期公司要做报表功能,在网上搜索下表格的样式后便自己写了一个自定义的表格控件,该表格控件能根据设置的数据中数据的最大值自动设置左侧信息栏显示
- 本文主要为大家分析了图书商城的用户模块,具体内容如下1、用户模块的相关类创建domain:Userdao:UserDaoservice:Us
- 一、引言大家都知道单例模式,通过一个全局变量来避免重复创建对象而产生的消耗,若系统存在大量的相似对象时,又该如何处理?参照单例模式,可通过对
- 在C#中,@符号不仅可以加在字符串常量之前,使字符串不作转义之用,还可以加在变量名之前,使变量名与关键字不冲突,这种用法称为“逐字标识符”。
- 最近看spring的JDBCTemplete的模板方式调用时,对模板和回调产生了浓厚兴趣,查询了一些资料,做一些总结。回调函数:所谓回调,就