diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java index f79c01fd6..71c13c8e3 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java @@ -35,15 +35,13 @@ import org.springframework.lang.NonNull; import org.springframework.util.CollectionUtils; - /** * 1. Listen to the Polaris server configuration publishing event 2. Write the changed * configuration content to propertySource 3. Refresh the context through contextRefresher * * @author lepdou 2022-03-28 */ -public abstract class PolarisConfigPropertyAutoRefresher - implements ApplicationListener, PolarisConfigPropertyRefresher { +public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationListener, PolarisConfigPropertyRefresher { private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfigPropertyAutoRefresher.class); @@ -53,9 +51,7 @@ public abstract class PolarisConfigPropertyAutoRefresher private final AtomicBoolean registered = new AtomicBoolean(false); - public PolarisConfigPropertyAutoRefresher( - PolarisConfigProperties polarisConfigProperties, - PolarisPropertySourceManager polarisPropertySourceManager) { + public PolarisConfigPropertyAutoRefresher(PolarisConfigProperties polarisConfigProperties, PolarisPropertySourceManager polarisPropertySourceManager) { this.polarisConfigProperties = polarisConfigProperties; this.polarisPropertySourceManager = polarisPropertySourceManager; } @@ -80,52 +76,75 @@ public abstract class PolarisConfigPropertyAutoRefresher } // register polaris config publish event + registerPolarisConfigPublishChangeListener(polarisPropertySources); + + // 增加接收刷新事件,接收到事件以后就进行全量配置刷新,重新加载PolarisConfigFileLocator,同时添加Listener事件 + new Thread(new Runnable() { + + @Override + public void run() { + for (; ; ) { + try { + List polarisPropertySourcesList = PolarisContextRefreshUtil.getRegisterPolarisPropertySourceQueue() + .take(); + LOGGER.info("[SCT Config] receive from register queue And start refresh All Config, polarisPropertySourcesList size:{}", polarisPropertySourcesList.size()); + refreshConfigurationProperties(null); + LOGGER.info("[SCT Config] start to register configFile polarisPropertySourcesList:{}", polarisPropertySourcesList); + registerPolarisConfigPublishChangeListener(polarisPropertySourcesList); + } + catch (Exception e) { + LOGGER.error("[SCT Config] receive from register queue exception:", e); + } + } + } + }).start(); + } + + private void registerPolarisConfigPublishChangeListener(List polarisPropertySources) { for (PolarisPropertySource polarisPropertySource : polarisPropertySources) { - polarisPropertySource.getConfigKVFile() - .addChangeListener((ConfigKVFileChangeListener) configKVFileChangeEvent -> { - - LOGGER.info( - "[SCT Config] received polaris config change event and will refresh spring context." - + " namespace = {}, group = {}, fileName = {}", - polarisPropertySource.getNamespace(), - polarisPropertySource.getGroup(), - polarisPropertySource.getFileName()); - - Map source = polarisPropertySource.getSource(); - - for (String changedKey : configKVFileChangeEvent.changedKeys()) { - ConfigPropertyChangeInfo configPropertyChangeInfo = configKVFileChangeEvent - .getChangeInfo(changedKey); - - LOGGER.info("[SCT Config] changed property = {}", configPropertyChangeInfo); - - // 新增动态改变日志级别的能力 - try { - if (changedKey.startsWith("logging.level") && changedKey.length() >= 14) { - String loggerName = changedKey.substring(14); - String newValue = configPropertyChangeInfo.getNewValue(); - LOGGER.info("[SCT Config] set logging.level loggerName:{}, newValue:{}", loggerName, newValue); - PolarisConfigLoggerContext.setLevel(loggerName, newValue); - } - } - catch (Exception e) { - LOGGER.error("[SCT Config] set logging.level exception,", e); - } - switch (configPropertyChangeInfo.getChangeType()) { - case MODIFIED: - case ADDED: - source.put(changedKey, configPropertyChangeInfo.getNewValue()); - break; - case DELETED: - source.remove(changedKey); - break; - } - // update the attribute with @Value annotation - refreshSpringValue(changedKey); + ConfigKVFileChangeListener configKVFileChangeListener = configKVFileChangeEvent -> { + + LOGGER.info("[SCT Config] received polaris config change event and will refresh spring context." + " namespace = {}, group = {}, fileName = {}", polarisPropertySource.getNamespace(), polarisPropertySource.getGroup(), polarisPropertySource.getFileName()); + + Map source = polarisPropertySource.getSource(); + + for (String changedKey : configKVFileChangeEvent.changedKeys()) { + ConfigPropertyChangeInfo configPropertyChangeInfo = configKVFileChangeEvent.getChangeInfo(changedKey); + + LOGGER.info("[SCT Config] changed property = {}", configPropertyChangeInfo); + + // 新增动态改变日志级别的能力 + try { + if (changedKey.startsWith("logging.level") && changedKey.length() >= 14) { + String loggerName = changedKey.substring(14); + String newValue = (String) configPropertyChangeInfo.getNewValue(); + LOGGER.info("[SCT Config] set logging.level loggerName:{}, newValue:{}", loggerName, newValue); + PolarisConfigLoggerContext.setLevel(loggerName, newValue); } - // update @ConfigurationProperties beans - refreshConfigurationProperties(configKVFileChangeEvent.changedKeys()); - }); + } + catch (Exception e) { + LOGGER.error("[SCT Config] set logging.level exception,", e); + } + switch (configPropertyChangeInfo.getChangeType()) { + case MODIFIED: + case ADDED: + source.put(changedKey, configPropertyChangeInfo.getNewValue()); + break; + case DELETED: + source.remove(changedKey); + break; + } + // update the attribute with @Value annotation + refreshSpringValue(changedKey); + } + // update @ConfigurationProperties beans + refreshConfigurationProperties(configKVFileChangeEvent.changedKeys()); + }; + polarisPropertySource.getConfigKVFile().addChangeListener(configKVFileChangeListener); + + // 保存最近添加的ChangeListener + PolarisContextRefreshUtil.getLastPolarisPropertyConfigKVFileMap() + .put(polarisPropertySource, configKVFileChangeListener); } } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisContextRefreshUtil.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisContextRefreshUtil.java new file mode 100644 index 000000000..ecb7c8049 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisContextRefreshUtil.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 www.tencent.com. + * All Rights Reserved. + * This program is the confidential and proprietary information of + * www.tencent.com ("Confidential Information"). You shall not disclose such + * Confidential Information and shall use it only in accordance with + * the terms of the license agreement you entered into with www.tencent.com. + */ +package com.tencent.cloud.polaris.config.adapter; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.LinkedBlockingQueue; + +import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener; + +/** + * @author juanyinyang + */ +public final class PolarisContextRefreshUtil { + + // 最近一次的全量PolarisPropertySource集合(PolarisPropertySource按 namespace + fileGroup + fileName 确保唯一) + private static final Map lastPolarisPropertyConfigKVFileMap = new LinkedHashMap<>(); + // 命名空间分组(namespace + fileGroup)的去重Set集合,如果这个分组已添加了ConfigFileGroupListener + private static final Set existConfigFileGroupListenerSet = new ConcurrentHashSet<>(); + // Queue里存放的是需要添加的配置列表集合(这类配置需要重新注册Listener) + private static final LinkedBlockingQueue> registerPolarisPropertySourceQueue = new LinkedBlockingQueue<>(100); + + private PolarisContextRefreshUtil() { + + } + + public static Map getLastPolarisPropertyConfigKVFileMap() { + return lastPolarisPropertyConfigKVFileMap; + } + + public static Set getExistConfigFileGroupListenerSet() { + return existConfigFileGroupListenerSet; + } + + public static LinkedBlockingQueue> getRegisterPolarisPropertySourceQueue() { + return registerPolarisPropertySourceQueue; + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySource.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySource.java index 7a47fd187..4e27aa762 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySource.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySource.java @@ -19,11 +19,13 @@ package com.tencent.cloud.polaris.config.adapter; import java.util.Map; +import java.util.Objects; import com.tencent.polaris.configuration.api.core.ConfigKVFile; import org.springframework.core.env.MapPropertySource; + /** * a polaris config file will be wrapped as polaris property source. * @@ -39,8 +41,7 @@ public class PolarisPropertySource extends MapPropertySource { private final ConfigKVFile configKVFile; - public PolarisPropertySource(String namespace, String group, String fileName, - ConfigKVFile configKVFile, Map source) { + public PolarisPropertySource(String namespace, String group, String fileName, ConfigKVFile configKVFile, Map source) { super(namespace + "-" + group + "-" + fileName, source); this.namespace = namespace; @@ -69,9 +70,31 @@ public class PolarisPropertySource extends MapPropertySource { return configKVFile; } + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(fileName, group, namespace); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PolarisPropertySource other = (PolarisPropertySource) obj; + return Objects.equals(fileName, other.fileName) && Objects.equals(group, other.group) && Objects.equals(namespace, other.namespace); + } + @Override public String toString() { - return "PolarisPropertySource{" + "namespace='" + namespace + '\'' + ", group='" - + group + '\'' + ", fileName='" + fileName + '\'' + '}'; + return "PolarisPropertySource{" + "namespace='" + namespace + '\'' + ", group='" + group + '\'' + ", fileName='" + fileName + '\'' + '}'; } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java index 24ad04d76..562fe9d1b 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java @@ -18,16 +18,14 @@ package com.tencent.cloud.polaris.config.listener; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import com.google.common.collect.Sets; -import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener; -import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -38,7 +36,9 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component; import org.springframework.test.context.junit.jupiter.SpringExtension; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; +import com.google.common.collect.Sets; +import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener; +import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; /** * Integration testing for change listener. @@ -104,7 +104,7 @@ public class ConfigChangeListenerTest { @PolarisConfigKVFileChangeListener(interestedKeys = {"timeout"}) public void configChangedListener(ConfigChangeEvent event) { ConfigPropertyChangeInfo changeInfo = event.getChange("timeout"); - timeout = Integer.parseInt(changeInfo.getNewValue()); + timeout = Integer.parseInt((String)changeInfo.getNewValue()); changeCnt++; hits.countDown(); } @@ -112,7 +112,7 @@ public class ConfigChangeListenerTest { @PolarisConfigKVFileChangeListener(interestedKeyPrefixes = {"timeout"}) public void configChangedListener2(ConfigChangeEvent event) { ConfigPropertyChangeInfo changeInfo = event.getChange("timeout"); - timeout = Integer.parseInt(changeInfo.getNewValue()); + timeout = Integer.parseInt((String)changeInfo.getNewValue()); changeCnt++; hits.countDown(); }