alibaba/spring-cloud-alibaba

Nacos Config配置监听比Spring的Environment要快,导致获取的配置还是旧的。

Closed this issue · 10 comments

Which Component

  • Nacos Config
  • 版本:2021.0.1.0

Describe the bug
由于业务场景需要监听nacos的配置改变,做了以下代码处理:

    private void addListener(ConfigService configService, Executor executor, NacosConfigProperties.Config config) {
        String dataId = config.getDataId();
        configService.addListener(dataId, config.getGroup(), new Listener() {
            @Override
            public Executor getExecutor() {
                return executor;
            }

            @SneakyThrows
            @Override
            public void receiveConfigInfo(String configInfo) {
                ConfigListener.ChangedEvent event = new ConfigListener.ChangedEvent(dataId, configInfo);
                eventPublisher.publishEvent(event);
                System.out.println("这是nacos监听器:" + configInfo);
                listenerOptional.ifPresent(listener -> listener.onChanged(event));
            }
        });
    }

当配置修改后,通过Spring的Environment获取配置,发现这时候拿到的配置还是旧的,代码如下:

    private final Environment environment;

    @SneakyThrows
    @Override
    public void onChanged(ChangedEvent event) {
        ApaasApplicationProperties applicationProperties = Binder.get(environment).bindOrCreate(
                "apaas", Bindable.of(ApaasApplicationProperties.class)
        );
        System.out.println(JSON.toJSONString(applicationProperties));
    }

但如果我在监听代码中睡眠一定时间(如2秒),则能正常拿到最新的配置,睡眠代码如下:
image

这是我通过日志观察到的效果:
Snipaste_2024-12-10_17-06-10

Expected behavior
在监听到nacos配置有变化时,通过Spring的Environment能拿到最新的配置。

从机制上,nacos的配置是最终一致性,nacos多个监听器之间并没有依赖关系,nacos保证的是通过监听器receiveConfigInfo接收到的是最新的配置,但不保证其他的监听器(更新Spring Enviroment)已经接受到了最新的结果

这个机制是否有可能做些调整,毕竟nacos的配置最终还是要刷新到Spring Environment中?

Environment

这个机制不好调整,现在你遇到的问题本质上是使用方式不对

Environment

这个机制不好调整,现在你遇到的问题本质上是使用方式不对

请问我的正确姿势是什么?意思是直接用nacos监听到的配置,而不是直接使用Environment吗?

Environment

这个机制不好调整,现在你遇到的问题本质上是使用方式不对

请问我的正确姿势是什么?意思是直接用nacos监听到的配置,而不是直接使用Environment吗?

我记得文档中刷新配置是基于@RefreshScope实现的, 你可以监听一下这个事件试一下EnvironmentChangeEvent

监听

可以使用sca发布的新的nacos注解,https://mp.weixin.qq.com/s/VJrM53Z-gfq94pYVEACdiQ ,看看是不是你需要的功能。

  • @NacosConfig:作用于 SpringBean 的字段,将 Nacos 中指定的配置注入字段;作用于 SpringBean Class,将 Nacos 中指定的配置注入 Bean 的属性中;作用于 FactoryBean 方法,将 Nacos 中指定的配置注入 Bean 的属性中,不依赖 RefreshScope 注解即可生效。

  • @NacosConfigListener:作用于 SpringBean 的方法,在 Nacos 中的配置发生变化时,以方法参数形式接受变更后的最新配置内容,支持以对象类型接收结果。

  • @NacosConfigKeysListener:作用于 SpringBean 的方法,在 Nacos 中的配置的指定属性 key 集合发生变化时,以方法参数 ConfigChangeEvent 接受变更前后的属性值。

用法实例:
@NacosConfig(dataId="apaas.application.json", group="apaas")
@bean
ApaasApplicationProperties apaasProperties(){
return new ApaasApplicationProperties();
}

保证在nacos创建的dataId="apaas.application.json", group="apaas" 的配置可以通过json反序列为你的ApaasApplicationProperties对象即可

你现在的这个用法用Spring的ConfigurationProperties 注解就行了。。。不需要那些监听回调的逻辑
@ConfigurationProperties(prefix="apaas")
public class ApaasApplicationProperties{
xxx
}

你现在的这个用法用Spring的ConfigurationProperties 注解就行了。。。不需要那些监听回调的逻辑 @ConfigurationProperties(prefix="apaas") public class ApaasApplicationProperties{ xxx }

感谢回复。这种确实可以拿到最新的配置,但是其实我想拿到配置改变的事件,以便于通知其他的业务处理。

或许使用EnvironmentChangeEvent是好的方式,但这种方式又拿不到具体的dataId,先这样吧…… 😂

  • @NacosConfigListener:作用于 SpringBean 的方法,在 Nacos 中的配置发生变化时,以方法参数形式接受变更后的最新配置内容,支持以对象类型接收结果。

如果你需要接受变更事件,做额外的业务通知,可以使用 新发布的注解
@NacosConfigListener:作用于 SpringBean 的方法,在 Nacos 中的配置发生变化时,以方法参数形式接受变更后的最新配置内容,支持以对象类型接收结果。