当我们把nacos服务端启动起来,项目中也集成好之后,兴高采烈的启动项目准备试一下,发现在nacos中修改配置之后发现项目中的配置竟然没有刷新,然后开始怀疑是不是自己那里配置的不对、那个注解没有写、nacos版本是不是和springboot不兼容,然后一通修改,最后发现还是不行,最后开始怀疑人生。
下面两个场景可能会帮到你
1.没有开启nacos的自动刷新
springboot集成nacos的时候,需要在项目的配置文件里加入如下配置
nacos:
config:
bootstrap:
enable: true
# nacos服务ip和端口
server-addr: 127.0.0.1:8848
data-id: test
group: local
username: nacos
password: nacos
type: yaml
# 开启nacos自动刷新,如果这个配置没有或者为false会导致配置不能自动刷新
auto-refresh: true
# 允许nacos服务端向本地同步配置
enable-remote-sync-config: true
并且在配置属性上使用@NacosValue注解并且配置注解的autoRefreshed的值为true(默认为false,不会自动刷新),两个配置缺一不可;如果你使用的是spring的@Value注解,只能获取到配置,但不能自动刷新配置。
到这里借本就可以解决99%的问题了,剩下的1%的问题接着往下看
2.项目中集成了ulisesbocchio或者类似的插件,对配置进行加密
如果不想看细节,只想解决问题,可以直接把项目中ulisesbocchio或者类似的插件移除掉,然后把加密的配置变成明文放到nacos中就好了。
------------------------下面进行问题定位------------------------
在nacos中修改配置后,会自动通知给客户端,客户端在收到通知变更的请求后,经过一系列的前戏会执行到
com.alibaba.nacos.client.config.impl.CacheData#safeNotifyListener这个方法里,源码如下我们在这个方法的第一行打一个断点追踪一下执行链路;
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>${jasypt-spring-boot-starter.version}</version>
</dependency>
private void safeNotifyListener(final String dataId, final String group, final String content, final String type,
final String md5, final String encryptedDataKey, final ManagerListenerWrap listenerWrap) {
final Listener listener = listenerWrap.listener;
if (listenerWrap.inNotifying) {
LOGGER.warn(
"[{}] [notify-currentSkip] dataId={}, group={}, md5={}, listener={}, listener is not finish yet,will try next time.",
name, dataId, group, md5, listener);
return;
}
//这里创建了一个线程,异步的修改配置,保证保证本次配置变更请求能够快速响应
Runnable job = new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader appClassLoader = listener.getClass().getClassLoader();
try {
if (listener instanceof AbstractSharedListener) {
AbstractSharedListener adapter = (AbstractSharedListener) listener;
adapter.fillContext(dataId, group);
LOGGER.info("[{}] [notify-context] dataId={}, group={}, md5={}", name, dataId, group, md5);
}
// Before executing the callback, set the thread classloader to the classloader of
// the specific webapp to avoid exceptions or misuses when calling the spi interface in
// the callback method (this problem occurs only in multi-application deployment).
Thread.currentThread().setContextClassLoader(appClassLoader);
ConfigResponse cr = new ConfigResponse();
cr.setDataId(dataId);
cr.setGroup(group);
cr.setContent(content);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
String contentTmp = cr.getContent();
listenerWrap.inNotifying = true;
// contentTmp这个变量里存储了最新的全量配置,也是我们这次源码追踪的重点,看看是如何自动刷新配置的
listener.receiveConfigInfo(contentTmp);
// compare lastContent and content
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;
LOGGER.info("[{}] [notify-ok] dataId={}, group={}, md5={}, listener={} ,cost={} millis.", name,
dataId, group, md5, listener, (System.currentTimeMillis() - start));
} catch (NacosException ex) {
LOGGER.error("[{}] [notify-error] dataId={}, group={}, md5={}, listener={} errCode={} errMsg={}",
name, dataId, group, md5, listener, ex.getErrCode(), ex.getErrMsg());
} catch (Throwable t) {
LOGGER.error("[{}] [notify-error] dataId={}, group={}, md5={}, listener={} tx={}", name, dataId,
group, md5, listener, t.getCause());
} finally {
listenerWrap.inNotifying = false;
Thread.currentThread().setContextClassLoader(myClassLoader);
}
}
};
final long startNotify = System.currentTimeMillis();
try {
if (null != listener.getExecutor()) {
//将上面创建的线程放到线程池里执行
listener.getExecutor().execute(job);
} else {
try {
INTERNAL_NOTIFIER.submit(job);
} catch (RejectedExecutionException rejectedExecutionException) {
LOGGER.warn(
"[{}] [notify-blocked] dataId={}, group={}, md5={}, listener={}, no available internal notifier,will sync notifier ",
name, dataId, group, md5, listener);
job.run();
} catch (Throwable throwable) {
LOGGER.error(
"[{}] [notify-blocked] dataId={}, group={}, md5={}, listener={}, submit internal async task fail,throwable= ",
name, dataId, group, md5, listener, throwable);
job.run();
}
}
} catch (Throwable t) {
LOGGER.error("[{}] [notify-error] dataId={}, group={}, md5={}, listener={} throwable={}", name, dataId,
group, md5, listener, t.getCause());
}
final long finishNotify = System.currentTimeMillis();
LOGGER.info("[{}] [notify-listener] time cost={}ms in ClientWorker, dataId={}, group={}, md5={}, listener={} ",
name, (finishNotify - startNotify), dataId, group, md5, listener);
}
由于篇幅问题,后面的我用图片展示,大家根据图片可以自行追踪
代码调试完成后,问题的根源也就找到了,接下来就是删除配置加密组件,修改为明文配置,启动项目,搞定收工。