没有合适的资源?快使用搜索试试~ 我知道了~
首页Nacos动态配置原理分析
Nacos动态配置原理分析
需积分: 50 3.6k 浏览量
更新于2023-05-25
评论
收藏 2.01MB PDF 举报
客户端是通过一个定时任务来检查自己监听的配置项的数据的,一旦服务端的数据发生变化时,客户端 将会获取到最新的数据,并将最新的数据保存在一个 CacheData 对象中,然后会重新计算 CacheData 的 md5 属性的值,此时就会对该 CacheData 所绑定的 Listener 触发 receiveConfigInfo 回调。
资源详情
资源评论
资源推荐

Nacos动态配置原理分析
一、流程分析
1、入口:NacosConfigAutoConfiguration
首先看一下spring-cloud-starter-alibaba-nacos-discovery项目的结构
看spring.factories中内容,优先加载装配的配置
NacosConfigAutoConfiguration 自动装配配置,进行Bean的初始化
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.alibaba.nacos.NacosConfigBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.nacos.NacosConfigAutoConfiguration,\
org.springframework.cloud.alibaba.nacos.endpoint.NacosConfigEndpointAutoConfigur
ation
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.cloud.alibaba.nacos.diagnostics.analyzer.NacosConnectionFail
ureAnalyzer

2、远程配置的加载:NacosConfigBootstrapConfiguration
首先远程配置的加载,spring boot通过NacosConfigBootstrapConfiguration中
NacosPropertySourceLocator类实现PropertySourceLocator接口作为入口,具体实现override
locate方法
@Configuration
@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled",
matchIfMissing = true)
public class NacosConfigAutoConfiguration {
@Bean
public NacosConfigProperties nacosConfigProperties(ApplicationContext
context) {
if (context.getParent() != null
&& BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
context.getParent(), NacosConfigProperties.class).length
> 0) {
return
BeanFactoryUtils.beanOfTypeIncludingAncestors(context.getParent(),
NacosConfigProperties.class);
}
NacosConfigProperties nacosConfigProperties = new
NacosConfigProperties();
return nacosConfigProperties;
}
@Bean
public NacosRefreshProperties nacosRefreshProperties() {
return new NacosRefreshProperties();
}
@Bean
public NacosRefreshHistory nacosRefreshHistory() {
return new NacosRefreshHistory();
}
@Bean
public NacosContextRefresher nacosContextRefresher(
NacosConfigProperties nacosConfigProperties,
NacosRefreshProperties nacosRefreshProperties,
NacosRefreshHistory refreshHistory) {
return new NacosContextRefresher(nacosRefreshProperties, refreshHistory,
nacosConfigProperties.configServiceInstance());
}
}

发现都调了nacosPropertySourceBuilder.build(dataId, group, fileExtension, true);
NacosPropertySourceBuilder中build通过configService.getConfig获取到远程配置。
NacosPropertySource build(String dataId, String group, String fileExtension,
boolean isRefreshable) {
Properties p = loadNacosData(dataId, group, fileExtension);
NacosPropertySource nacosPropertySource = new NacosPropertySource(group,
dataId,
propertiesToMap(p), new Date(), isRefreshable);
NacosPropertySourceRepository.collectNacosPropertySources(nacosPropertySource);
return nacosPropertySource;
}
private Properties loadNacosData(String dataId, String group, String
fileExtension) {
String data = null;
try {
data = configService.getConfig(dataId, group, timeout);
if (!StringUtils.isEmpty(data)) {
log.info(String.format("Loading nacos data, dataId: '%s', group:
'%s'",
dataId, group));
if (fileExtension.equalsIgnoreCase("properties")) {
Properties properties = new Properties();

3、@RefreshScope配置热更新实现原理
RefreshScope:自定义Bean的工作区域,spring默认有单例、原型,还支持扩展Bean Scope,
RefreshScope其中一种
RefreshScope作用:可在Bean运行期间动态刷新
properties.load(new StringReader(data));
return properties;
}
else if (fileExtension.equalsIgnoreCase("yaml")
|| fileExtension.equalsIgnoreCase("yml")) {
YamlPropertiesFactoryBean yamlFactory = new
YamlPropertiesFactoryBean();
yamlFactory.setResources(new
ByteArrayResource(data.getBytes()));
return yamlFactory.getObject();
}
}
}
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 EMPTY_PROPERTIES;
}
private void registerNacosListener(final String group, final String dataId) {
// 默认匿名监听器
Listener listener = listenerMap.computeIfAbsent(dataId, i -> new Listener()
{
@Override
public void receiveConfigInfo(String configInfo) {
refreshCountIncrement();
String md5 = "";
if (!StringUtils.isEmpty(configInfo)) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md5 = new BigInteger(1, md.digest(configInfo.getBytes("UTF-
8")))
.toString(16);
}
catch (NoSuchAlgorithmException | UnsupportedEncodingException
e) {
log.warn("[Nacos] unable to get md5 for dataId: " + dataId,
e);
}
}
refreshHistory.add(dataId, md5);
//发布刷新事件
applicationContext.publishEvent(
new RefreshEvent(this, null, "Refresh Nacos config"));

RefreshEvent事件监听处理:
org.springframework.cloud.endpoint.event.RefreshEventListener.onApplicationEvent
if (log.isDebugEnabled()) {
log.debug("Refresh Nacos config group " + group + ",dataId" +
dataId);
}
}
@Override
public Executor getExecutor() {
return null;
}
});
try {
configService.addListener(dataId, group, listener);
}
catch (NacosException e) {
e.printStackTrace();
}
}
// 刷新事件处理
public void handle(RefreshEvent event) {
if (this.ready.get()) { // don't handle events before app is ready
log.debug("Event received " + event.getEventDesc());
// 刷新具体逻辑
Set<String> keys = this.refresh.refresh();
log.info("Refresh keys changed: " + keys);
}
}
//org.springframework.cloud.context.refresh.ContextRefresher.refresh
public synchronized Set<String> refresh() {
// 刷新配置,相当于NacosPropertySourceLocator需重新从Nacos配置中心拉取数据
Set<String> keys = refreshEnvironment();
//refreshScope 重新刷新
this.scope.refreshAll();
return keys;
}
//this.scope.refreshAll();
//org.springframework.cloud.context.scope.refresh.RefreshScope.refreshAll
public void refreshAll() {
super.destroy();
// 发布RefreshScopeRefreshedEvent事件,spring默认无监听器,若有特殊业务处理可监
听,例如Eureka
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}
//父类destroy: org.springframework.cloud.context.scope.GenericScope.destroy()
//这里就先把cache清空了,返回了BeanLifecycleWrapper集合。然后拿出前面创建的对应的写锁,上锁
后,进行BeanLifecycleWrapper的销毁。
@Override
剩余22页未读,继续阅读


















安全验证
文档复制为VIP权益,开通VIP直接复制

评论0