Nacos文件需要自动刷新时,整合SpringCloud使用@RefreshScope注解https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html,其通过com.alibaba.cloud.nacos.NacosConfigAutoConfiguration完成相关类的自动注入;只使用SpringBoot时使用@NacosValue(autoRefreshed = true)注解,默认autoRefreshed 为false,不刷新https://nacos.io/zh-cn/docs/quick-start-spring-boot.html,其通过com.alibaba.boot.nacos.config.autoconfigure.NacosConfigAutoConfiguration(@EnableNacosConfig),完成相关类的自动注入;以整合SpringBoot为例。
NacosConfigService的初始化是在NacosConfigManager构造函数中完成的,其通过
NacosConfigProperties (bootstrap文件中的spring.cloud.nacos.config)配置的相关信息进行实例化。NacosConfigService中构建了
ClientWorker客户端,定时更新配置文件。
public NacosConfigManager(NacosConfigProperties nacosConfigProperties) {
this.nacosConfigProperties = nacosConfigProperties;
createConfigService(nacosConfigProperties);
}
static ConfigService createConfigService(NacosConfigProperties nacosConfigProperties) {
if(Objects.isNull(service)) {
Class var1 = NacosConfigManager.class;
synchronized(NacosConfigManager.class) {
try {
if(Objects.isNull(service)) {
service = NacosFactory.createConfigService(nacosConfigProperties.assembleConfigServiceProperties());
}
} catch (NacosException var4) {
}
}
}
return service;
}
public NacosConfigService(Properties properties) throws NacosException {
String encodeTmp = properties.getProperty("encode");
if(StringUtils.isBlank(encodeTmp)) {
this.encode = "UTF-8";
} else {
this.encode = encodeTmp.trim();
}
this.initNamespace(properties);
//生成对Nacos服务端http请求的代理类
this.agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
this.agent.start();
//构建ClientWorker客户端,实时更新配置文件
this.worker = new ClientWorker(this.agent, this.configFilterChainManager, properties);
}
public ClientWorker(final HttpAgent agent, ConfigFilterChainManager configFilterChainManager, Properties properties) {
this.agent = agent;
this.configFilterChainManager = configFilterChainManager;
this.init(properties);
this.executor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("com.alibaba.nacos.client.Worker." + agent.getName());
t.setDaemon(true);
return t;
}
});
this.executorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("com.alibaba.nacos.client.Worker.longPolling." + agent.getName());
t.setDaemon(true);
return t;
}
});
//定时检测远端配置是否发生改变,
this.executor.scheduleWithFixedDelay(new Runnable() {
public void run() {
try {
ClientWorker.this.checkConfigInfo();
} catch (Throwable var2) {
ClientWorker.LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", var2);
}
}
}, 1L, 10L, TimeUnit.MILLISECONDS);
}
ClientWorker.checkConfigInfo
public void checkConfigInfo() {
//监听的文件的个数
int listenerSize = ((Map)this.cacheMap.get()).size();
//监听的文件的个数/设置的单次请求处理的个数=向上取整得到任务个数
int longingTaskCount = (int)Math.ceil((double)listenerSize / ParamUtil.getPerTaskConfigSize());
if((double)longingTaskCount > this.currentLongingTaskCount) {
for(int i = (int)this.currentLongingTaskCount; i < longingTaskCount; ++i) {
this.executorService.execute(new ClientWorker.LongPollingRunnable(i));
}
this.currentLongingTaskCount = (double)longingTaskCount;
}
}
class LongPollingRunnable implements Runnable {
private int taskId;
public LongPollingRunnable(int taskId) {
this.taskId = taskId;
}
public void run() {
ArrayList cacheDatas = new ArrayList();
ArrayList inInitializingCacheList = new ArrayList();
try {
Iterator e = ((Map)ClientWorker.this.cacheMap.get()).values().iterator();
while(e.hasNext()) {
CacheData cacheData = (CacheData)e.next();
if(cacheData.getTaskId() == this.taskId) {
cacheDatas.add(cacheData);
try {
//检查本地的配置文件数据和缓存中的最新数据是否一致,判断配置文件数据是否变化
ClientWorker.this.checkLocalConfig(cacheData);
if(cacheData.isUseLocalConfigInfo()) {//已经变化
//检查文件的文件是否改变,如果改变,通知监听了这个配置文件的监听器
cacheData.checkListenerMd5();
}
}
}
}
//向Nacos服务发送请求,检查cacheDatas中的配置文件哪些发生了变化
List e1 = ClientWorker.this.checkUpdateDataIds(cacheDatas, inInitializingCacheList);
ClientWorker.LOGGER.info("get changedGroupKeys:" + e1);
Iterator cacheData2 = e1.iterator();
//遍历发生变化的cacheData
while(cacheData2.hasNext()) {
String cacheData1 = (String)cacheData2.next();
String[] key = GroupKey.parseKey(cacheData1);
String dataId = key[0];
String group = key[1];
String tenant = null;
if(key.length == 3) {
tenant = key[2];
}
try {
//发送请求,获取最新的配置信息
String[] ioe = ClientWorker.this.getServerConfig(dataId, group, tenant, 3000L);
CacheData message1 = (CacheData)((Map)ClientWorker.this.cacheMap.get()).get(GroupKey.getKeyTenant(dataId, group, tenant));
//更新缓存中CacheData 的content内容及MD5值
message1.setContent(ioe[0]);
if(null != ioe[1]) {
message1.setType(ioe[1]);
}
}
}
} catch (Throwable var14) {
ClientWorker.this.executorService.schedule(this, (long)ClientWorker.this.taskPenaltyTime, TimeUnit.MILLISECONDS);
}
}
}
List<String> checkUpdateDataIds(List<CacheData> cacheDatas, List<String> inInitializingCacheList) throws IOException {
StringBuilder sb = new StringBuilder();
Iterator isInitializingCacheList = cacheDatas.iterator();
while(isInitializingCacheList.hasNext()) {
CacheData cacheData = (CacheData)isInitializingCacheList.next();
//使用间隔符拼接请求cacheData的dataId、group、Md5的值作为参数,服务端获取对应的文件后,比对MD5值,判断文件是否发生改变
//extension-config.propertiesDEFAULT_GROUPnacos-demo.yamlDEFAULT_GROUPshared-config
if(!cacheData.isUseLocalConfigInfo()) {
sb.append(cacheData.dataId).append(Constants.WORD_SEPARATOR);
sb.append(cacheData.group).append(Constants.WORD_SEPARATOR);
if(StringUtils.isBlank(cacheData.tenant)) {
sb.append(cacheData.getMd5()).append(Constants.LINE_SEPARATOR);
} else {
sb.append(cacheData.getMd5()).append(Constants.WORD_SEPARATOR);
sb.append(cacheData.getTenant()).append(Constants.LINE_SEPARATOR);
}
if(cacheData.isInitializing()) {
inInitializingCacheList.add(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant));
}
}
}
boolean isInitializingCacheList1 = !inInitializingCacheList.isEmpty();
return this.checkUpdateConfigStr(sb.toString(), isInitializingCacheList1);
}
CacheData.checkListenerMd5方法,用于通过比对MD5,判断监听器中保存的配置和最新的配置文件是不是相同,如果发生改变,回调对应的监听器
void checkListenerMd5() {
Iterator var1 = this.listeners.iterator();
while(var1.hasNext()) {
ManagerListenerWrap wrap = (ManagerListenerWrap)var1.next();
if(!this.md5.equals(wrap.lastCallMd5)) {
this.safeNotifyListener(this.dataId, this.group, this.content, this.type, this.md5, wrap);
}
}
}
CacheData.safeNotifyListener
private void safeNotifyListener(final String dataId, final String group, final String content, final String type, final String md5, final ManagerListenerWrap listenerWrap) {
final Listener listener = listenerWrap.listener;
Runnable job = new Runnable() {
public void run() {
ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader appClassLoader = listener.getClass().getClassLoader();
try {
if(listener instanceof AbstractSharedListener) {
AbstractSharedListener t = (AbstractSharedListener)listener;
t.fillContext(dataId, group);
}
Thread.currentThread().setContextClassLoader(appClassLoader);
ConfigResponse t1 = new ConfigResponse();
t1.setDataId(dataId);
t1.setGroup(group);
t1.setContent(content);
CacheData.this.configFilterChainManager.doFilter((IConfigRequest)null, t1);
String contentTmp = t1.getContent();
//更新environment中的配置文件,listener的创建是在NacosPropertySourcePostProcessor.addListenerIfAutoRefreshed中完成的
listener.receiveConfigInfo(contentTmp);
if(listener instanceof AbstractConfigChangeListener) {
Map data = ConfigChangeHandler.getInstance().parseChangeData(listenerWrap.lastContent, content, type);
ConfigChangeEvent event = new ConfigChangeEvent(data);
((AbstractConfigChangeListener)listener).receiveConfigChange(event);
listenerWrap.lastContent = content;
}
listenerWrap.lastCallMd5 = md5;
} catch (NacosException var11) {
} catch (Throwable var12) {
} finally {
}
}
};
long startNotify = System.currentTimeMillis();
try {
if(null != listener.getExecutor()) {
listener.getExecutor().execute(job);
} else {
job.run();
}
} catch (Throwable var13) {
}
long finishNotify = System.currentTimeMillis();
}
DelegatingEventPublishingListener.receiveConfigInfo
public void receiveConfigInfo(String content) {
//替换environment中的propertySources的配置
this.onReceived(content);
//发送NacosConfigReceivedEvent事件,通知监听器
this.publishEvent(content);
}
NacosPropertySourcePostProcessor.addListenerIfAutoRefreshed,为ConfigService增加一个listener,当配置改变时回调listener的receiveConfigInfo更新environment中propertySources的文件
public static void addListenerIfAutoRefreshed(final NacosPropertySource nacosPropertySource, Properties properties, final ConfigurableEnvironment environment) {
if(nacosPropertySource.isAutoRefreshed()) {
final String dataId = nacosPropertySource.getDataId();
final String groupId = nacosPropertySource.getGroupId();
final String type = nacosPropertySource.getType();
NacosServiceFactory nacosServiceFactory = NacosBeanUtils.getNacosServiceFactoryBean(beanFactory);
try {
ConfigService e = nacosServiceFactory.createConfigService(properties);
AbstractListener listener = new AbstractListener() {
public void receiveConfigInfo(String config) {
String name = nacosPropertySource.getName();
NacosPropertySource newNacosPropertySource = new NacosPropertySource(dataId, groupId, name, config, type);
newNacosPropertySource.copy(nacosPropertySource);
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.replace(name, newNacosPropertySource);
}
};
if(e instanceof EventPublishingConfigService) {
((EventPublishingConfigService)e).addListener(dataId, groupId, type, listener);
} else {
e.addListener(dataId, groupId, listener);
}
} catch (NacosException var9) {
throw new RuntimeException("ConfigService can\'t add Listener with properties : " + properties, var9);
}
}
}
NacosValueAnnotationBeanPostProcessor.onApplicationEvent从event中获取最新的配置信息,转化为configProperties,通过配置的key从placeholderNacosValueTargetMap中获取需要更新配置文件的实例,并更新对应的属性值。
public void onApplicationEvent(NacosConfigReceivedEvent event) {
String content = event.getContent();
if (content != null) {
Properties configProperties = toProperties(content);
for (Object key : configProperties.keySet()) {
String propertyKey = (String)key;
List<NacosValueTarget> beanPropertyList = placeholderNacosValueTargetMap.get(propertyKey);
if (beanPropertyList == null) {
continue;
}
String propertyValue = configProperties.getProperty(propertyKey);
for (NacosValueTarget nacosValueTarget : beanPropertyList) {
if (nacosValueTarget.method == null) {
setField(nacosValueTarget, propertyValue);
} else {
setMethod(nacosValueTarget, propertyValue);
}
}
}
}
}