apolloconfig / apollo

Apollo is a reliable configuration management system suitable for microservice configuration management scenarios.
https://www.apolloconfig.com
Apache License 2.0
28.95k stars 10.19k forks source link

@ApolloConfigChangeListener刷新@ConfigurationProperties标记的类属性 #4994

Closed he928141842 closed 8 months ago

he928141842 commented 8 months ago

描述bug

使用@ApolloConfigChangeListener刷新@ConfigurationProperties标记的类属性,每一次修改观察日志平台输出,这行日志都会翻倍,并且cpu会不停的涨,第一次可能5%第二次可能20%,重复修改几次后直接cpu到100%,影响系统响应

复现

通过如下步骤可以复现:

  1. @ApolloConfigChangeListener(value = {"MaserDataApplication", "MdmMaserDataOperation"}) public void onChange(ConfigChangeEvent changeEvent) { for (String changedKey : changeEvent.changedKeys()) { log.info("MasterDataAppConfiguration.onChange >>> apollo changed namespace:{} Key:{} value:{}", changeEvent.getNamespace(), changedKey, changeEvent.getChange(changedKey)); } applicationEventPublisher.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys())); refreshScope.refreshAll(); }
  2. 多次修改监听的namespace的任意key

请问是这个用法有问题吗?

dosubot[bot] commented 8 months ago

🤖

您好,感谢您的反馈。从您的描述来看,这可能是一个性能问题。为了更好地理解和解决这个问题,我需要更多的信息。

  1. 您能否提供一下您的Apollo版本号?
  2. 您的应用程序是在什么样的环境下运行的?例如,操作系统,JVM版本等。
  3. 您是否有尝试过在不同的环境或Apollo版本下复现这个问题?
  4. 您是否有在其他应用程序中使用相同的代码,并且没有出现这个问题?
  5. 您能否提供一下具体的日志输出,以及CPU使用率的截图?

这些信息将有助于我们更好地理解问题,并找到解决方案。谢谢!


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

he928141842 commented 8 months ago

阿波罗版本1.7.2,jdk1.8.
今天似乎找到出问题代码是啥了,@ApolloConfigChangeListener 的onchange方法是写在一个具体的@ConfigurationProperties类中的。 @Configuration @ConfigurationProperties(prefix = "master.data.app.config") public class MasterDataAppConfiguration {

private final ApplicationEventPublisher applicationEventPublisher;

private final RefreshScope refreshScope;
/**
 * Map<AppId,PayHubApp>
 */
private Map<String, MasterDataApp> appMap = new HashMap<>();

public MasterDataAppConfiguration(ApplicationEventPublisher applicationEventPublisher,
                                   RefreshScope refreshScope) {
    this.applicationEventPublisher = applicationEventPublisher;
    this.refreshScope = refreshScope;
}

/**
 * 监听APP配置变动
 * @param changeEvent
 */
@ApolloConfigChangeListener(value = {"application", "MaserDataApplication", "MaserDataOperation"})
public void onChange(ConfigChangeEvent changeEvent) {
    for (String changedKey : changeEvent.changedKeys()) {
        log.info("MasterDataAppConfiguration.onChange >>> apollo changed namespace:{} Key:{} value:{}",
                changeEvent.getNamespace(), changedKey, changeEvent.getChange(changedKey));
    }
    applicationEventPublisher.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
    //refreshScope.refreshAll();
}

}

换了一种写法,把它单独拿到外面似乎程序就运行正常,不知道这是为什么。 @Component @Slf4j public class ApolloNamespaceRefresher {

@Autowired
private ApplicationEventPublisher applicationEventPublisher;

@Autowired
private RefreshScope refreshScope;

@ApolloConfigChangeListener(value = {"application", "MaserDataApplication", "MaserDataOperation"})
public void onChange(ConfigChangeEvent changeEvent) {
    for (String changedKey : changeEvent.changedKeys()) {
        log.info("MasterDataAppConfiguration.onChange >>> apollo changed namespace:{} Key:{} value:{}",
                changeEvent.getNamespace(), changedKey, changeEvent.getChange(changedKey));
    }
    applicationEventPublisher.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
    refreshScope.refreshAll();
}

}

nobodyiam commented 8 months ago

猜测是 MasterDataAppConfiguration 每次刷新后会重建,导致事件处理会持续增加,导致重复处理。 另外 refreshScope.refreshAll(); 应该是不需要的?

he928141842 commented 8 months ago

猜测是 MasterDataAppConfiguration 每次刷新后会重建,导致事件处理会持续增加,导致重复处理。 另外 refreshScope.refreshAll(); 应该是不需要的?

是的,排查代码发现ConfigService里面缓存的listener在重建bean时会一直增加,没有销毁。 refreshScope.refreshAll()不需要。