Nacos 动态刷新配置的原理基于 长轮询机制 和 Spring Cloud Context 的 @RefreshScope 实现。
原理解析
Nacos 配置中心 <-- 长轮询 Nacos 客户端监听到配置变化 --> 触发 Spring Context 的 RefreshEvent
–> RefreshScope` Bean 被重新加载 -->应用中的属性值被动态更新
一、Nacos 客户端监听到配置变化
1.1 启动阶段
通过NacosContextRefresher想nacos注册一个监听器,当有变化的时候,将会发布一个RefreshEvent事件。具体提代码见下:
com.alibaba.cloud.nacos.refresh.NacosContextRefresher#registerNacosListener
String key = NacosPropertySourceRepository.getMapKey(dataKey, groupKey);
Listener listener = listenerMap.computeIfAbsent(key,
lst -> new AbstractSharedListener() {
@Override
public void innerReceive(String dataId, String group,
String configInfo) {
// ...省略部分代码
//向spring发布一个RefreshEvent事件
applicationContext.publishEvent(
new RefreshEvent(this, null, "Refresh Nacos config"));
// ...省略部分代码
}
});
try {
configService.addListener(dataKey, groupKey, listener);
}
catch (NacosException e) {
// ...省略部分代码
}
1.2 Nacos 客户端的长轮询
LongPollingRunnable的run方法主要实现功能有,
1、调用nacos服务端接口获取配置项中对应md5值,如果与本地的配置比较有变化则存储为changedGroupKeys
2、对所有的changedGroupKeys获取服务器上最新的配置数据,并更新本地cacheData里面的内容及md5执行
3、执行cacheData.checkListenerMd5(),对有变化的数据通知给启动阶段的注册的监听器
com.alibaba.nacos.client.config.impl.ClientWorker.LongPollingRunnable#run
@Override
public void run() {
List<CacheData> cacheDatas = new ArrayList<CacheData>();
List<String> inInitializingCacheList = new ArrayList<String>();
try {
// check failover config
for (CacheData cacheData : cacheMap.get().values()) {
if (cacheData.getTaskId() == taskId) {
cacheDatas.add(cacheData);
// ... 省略部分代码
}
}
// check server config
List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList);
LOGGER.info("get changedGroupKeys:" + changedGroupKeys);
for (String groupKey : changedGroupKeys) {
String[] key = GroupKey.parseKey(groupKey);
String dataId = key[0];
String group = key[1];
String tenant = null;
if (key.length == 3) {
tenant = key[2];
}
try {
// 获取服务器上最新的配置
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
cache.setContent(ct[0]);
// ... 省略部分代码
}
for (CacheData cacheData : cacheDatas) {
if (