Add config change listener feature support (#299)

Co-authored-by: lepdou <ledouzhang@tencent.com>
pull/301/head
Haotian Zhang 2 years ago committed by GitHub
parent db45a57f18
commit eb72d49506
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,3 +5,4 @@
- [feat:support reading configuration from application.yml or application.properties.](https://github.com/Tencent/spring-cloud-tencent/pull/262) - [feat:support reading configuration from application.yml or application.properties.](https://github.com/Tencent/spring-cloud-tencent/pull/262)
- [fix:fix ClassNotFoundException while not importing openfeign when using circuit-breaker module.](https://github.com/Tencent/spring-cloud-tencent/pull/271) - [fix:fix ClassNotFoundException while not importing openfeign when using circuit-breaker module.](https://github.com/Tencent/spring-cloud-tencent/pull/271)
- [fix:solve ratelimit-callee-service UnknownHostException.](https://github.com/Tencent/spring-cloud-tencent/pull/292) - [fix:solve ratelimit-callee-service UnknownHostException.](https://github.com/Tencent/spring-cloud-tencent/pull/292)
- [Feature: Add config change listener feature support](https://github.com/Tencent/spring-cloud-tencent/pull/299)

@ -20,7 +20,9 @@ package com.tencent.cloud.polaris.config;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceAutoRefresher; import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceAutoRefresher;
import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager;
import com.tencent.cloud.polaris.config.annotation.PolarisConfigAnnotationProcessor;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.listener.PolarisConfigChangeEventListener;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -46,4 +48,14 @@ public class PolarisConfigAutoConfiguration {
contextRefresher); contextRefresher);
} }
@Bean
public PolarisConfigAnnotationProcessor polarisConfigAnnotationProcessor() {
return new PolarisConfigAnnotationProcessor();
}
@Bean
public PolarisConfigChangeEventListener polarisConfigChangeEventListener() {
return new PolarisConfigChangeEventListener();
}
} }

@ -0,0 +1,105 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.annotation;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent;
import com.tencent.cloud.polaris.config.listener.ConfigChangeListener;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.NonNull;
import org.springframework.util.ReflectionUtils;
import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.addChangeListener;
/**
* {@link PolarisConfigAnnotationProcessor} implementation for spring .
* <p>Refer to the Apollo project implementation
* <code><a href=https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloAnnotationProcessor.java>
* ApolloAnnotationProcessor</a></code>
* @author <a href="mailto:iskp.me@gmail.com">Palmer Xu</a> 2022-06-07
*/
public class PolarisConfigAnnotationProcessor implements BeanPostProcessor, PriorityOrdered {
@Override
public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName)
throws BeansException {
Class<?> clazz = bean.getClass();
for (Method method : findAllMethod(clazz)) {
this.processPolarisConfigChangeListener(bean, method);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
return bean;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
private List<Method> findAllMethod(Class<?> clazz) {
final List<Method> res = new LinkedList<>();
ReflectionUtils.doWithMethods(clazz, res::add);
return res;
}
private void processPolarisConfigChangeListener(final Object bean, final Method method) {
PolarisConfigKVFileChangeListener annotation = AnnotationUtils
.findAnnotation(method, PolarisConfigKVFileChangeListener.class);
if (annotation == null) {
return;
}
Class<?>[] parameterTypes = method.getParameterTypes();
Preconditions.checkArgument(parameterTypes.length == 1,
"Invalid number of parameters: %s for method: %s, should be 1", parameterTypes.length,
method);
Preconditions.checkArgument(ConfigChangeEvent.class.isAssignableFrom(parameterTypes[0]),
"Invalid parameter type: %s for method: %s, should be ConfigChangeEvent", parameterTypes[0],
method);
ReflectionUtils.makeAccessible(method);
String[] annotatedInterestedKeys = annotation.interestedKeys();
String[] annotatedInterestedKeyPrefixes = annotation.interestedKeyPrefixes();
ConfigChangeListener configChangeListener = changeEvent -> ReflectionUtils.invokeMethod(method, bean, changeEvent);
Set<String> interestedKeys =
annotatedInterestedKeys.length > 0 ? Sets.newHashSet(annotatedInterestedKeys) : null;
Set<String> interestedKeyPrefixes =
annotatedInterestedKeyPrefixes.length > 0 ? Sets.newHashSet(annotatedInterestedKeyPrefixes)
: null;
addChangeListener(configChangeListener, interestedKeys, interestedKeyPrefixes);
}
}

@ -0,0 +1,58 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Configuring the change listener annotation.
* <p>Refer to the Apollo project implementation
* <code><a href=https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/annotation/ApolloAnnotationProcessor.java>
* ApolloAnnotationProcessor</a></code>
* @author Palmer Xu 2022-05-31
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface PolarisConfigKVFileChangeListener {
/**
* The keys interested in the listener, will only be notified if any of the interested keys is changed.
* <br />
* If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when any key is changed.
* @return interested keys in the listener
*/
String[] interestedKeys() default {};
/**
* The key prefixes that the listener is interested in, will be notified if and only if the changed keys start with anyone of the prefixes.
* The prefixes will simply be used to determine whether the {@code listener} should be notified or not using {@code changedKey.startsWith(prefix)}.
* e.g. "spring." means that {@code listener} is interested in keys that starts with "spring.", such as "spring.banner", "spring.jpa", etc.
* and "application" means that {@code listener} is interested in keys that starts with "application", such as "applicationName", "application.port", etc.
* <br />
* If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when whatever key is changed.
* @return interested key-prefixed in the listener
*/
String[] interestedKeyPrefixes() default {};
}

@ -0,0 +1,88 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.listener;
import java.util.Map;
import java.util.Set;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
/**
* A change event when config is changed .
*
* @author <a href="mailto:iskp.me@gmail.com">Palmer Xu</a> 2022-06-07
*/
public final class ConfigChangeEvent {
/**
* all changes keys map.
*/
private final Map<String, ConfigPropertyChangeInfo> changes;
/**
* all interested changed keys.
*/
private final Set<String> interestedChangedKeys;
/**
* Config Change Event Constructor.
* @param changes all changes keys map
* @param interestedChangedKeys all interested changed keys
*/
public ConfigChangeEvent(Map<String, ConfigPropertyChangeInfo> changes, Set<String> interestedChangedKeys) {
this.changes = changes;
this.interestedChangedKeys = interestedChangedKeys;
}
/**
* Get the keys changed.
* @return the list of the keys
*/
public Set<String> changedKeys() {
return changes.keySet();
}
/**
* Get a specific change instance for the key specified.
* @param key the changed key
* @return the change instance
*/
public ConfigPropertyChangeInfo getChange(String key) {
return changes.get(key);
}
/**
* Check whether the specified key is changed .
* @param key the key
* @return true if the key is changed, false otherwise.
*/
public boolean isChanged(String key) {
return changes.containsKey(key);
}
/**
* Maybe subclass override this method.
*
* @return interested and changed keys
*/
public Set<String> interestedChangedKeys() {
return interestedChangedKeys;
}
}

@ -0,0 +1,35 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.listener;
/**
* Configuring the change listener interface.
*
* @author Palmer Xu 2022-05-31
*/
public interface ConfigChangeListener {
/**
* Invoked when there is any config change for the namespace.
*
* @param changeEvent the event for this change
*/
void onChange(ConfigChangeEvent changeEvent);
}

@ -0,0 +1,111 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.listener;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import com.google.common.collect.Maps;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.lang.NonNull;
import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.fireConfigChange;
import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.initialize;
import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.merge;
/**
* Polaris Config Change Event Listener .
*
* @author <a href="mailto:iskp.me@gmail.com">Elve.Xu</a> 2022-06-08
*/
public final class PolarisConfigChangeEventListener implements ApplicationListener<ApplicationEvent> {
private static final AtomicBoolean started = new AtomicBoolean();
/**
* Handle an application event.
*
* @param event the event to respond to
*/
@Override
public void onApplicationEvent(@NonNull ApplicationEvent event) {
// Initialize application all environment properties .
if (event instanceof ApplicationStartedEvent && started.compareAndSet(false, true)) {
ApplicationStartedEvent applicationStartedEvent = (ApplicationStartedEvent) event;
ConfigurableEnvironment environment = applicationStartedEvent.getApplicationContext().getEnvironment();
Map<String, Object> ret = loadEnvironmentProperties(environment);
if (!ret.isEmpty()) {
initialize(ret);
}
}
// Process Environment Change Event .
if (event instanceof EnvironmentChangeEvent) {
EnvironmentChangeEvent environmentChangeEvent = (EnvironmentChangeEvent) event;
ConfigurableApplicationContext context = (ConfigurableApplicationContext) environmentChangeEvent.getSource();
ConfigurableEnvironment environment = context.getEnvironment();
Map<String, Object> ret = loadEnvironmentProperties(environment);
Map<String, ConfigPropertyChangeInfo> changes = merge(ret);
fireConfigChange(changes.keySet(), Maps.newHashMap(changes));
changes.clear();
}
}
/**
* Try load all application environment config properties .
* @param environment application environment instance of {@link Environment}
* @return properties
*/
@SuppressWarnings("unchecked")
private Map<String, Object> loadEnvironmentProperties(ConfigurableEnvironment environment) {
Map<String, Object> ret = Maps.newHashMap();
MutablePropertySources sources = environment.getPropertySources();
sources.iterator().forEachRemaining(propertySource -> {
Object o = propertySource.getSource();
if (o instanceof Map) {
for (Map.Entry<String, Object> entry : ((Map<String, Object>) o).entrySet()) {
String key = entry.getKey();
String value = environment.getProperty(key);
ret.put(key, value);
}
}
else if (o instanceof Collection) {
int count = 0;
Collection<Object> collection = (Collection<Object>) o;
for (Object object : collection) {
String key = "[" + (count++) + "]";
ret.put(key, object);
}
}
});
return ret;
}
}

@ -0,0 +1,277 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.listener;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import static com.tencent.polaris.configuration.api.core.ChangeType.ADDED;
import static com.tencent.polaris.configuration.api.core.ChangeType.DELETED;
import static com.tencent.polaris.configuration.api.core.ChangeType.MODIFIED;
/**
* Polaris Config Listener Context Defined .
* <p>Refer to the Apollo project implementation
* <code><a href=https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfig.java>
* AbstractConfig</a></code>
* @author Palmer Xu 2022-06-06
*/
public final class PolarisConfigListenerContext {
/**
* Logger instance.
*/
private static final Logger LOG = LoggerFactory.getLogger(PolarisConfigListenerContext.class);
/**
* Execute service Atomic Reference Cache .
*/
private static final AtomicReference<ExecutorService> EAR = new AtomicReference<>();
/**
* All custom {@link ConfigChangeListener} instance defined in application .
*/
private static final List<ConfigChangeListener> listeners = Lists.newCopyOnWriteArrayList();
/**
* All custom interested keys defined in application .
*/
private static final Map<ConfigChangeListener, Set<String>> interestedKeys = Maps.newHashMap();
/**
* All custom interested key prefixes defined in application .
*/
private static final Map<ConfigChangeListener, Set<String>> interestedKeyPrefixes = Maps.newHashMap();
/**
* Cache all latest configuration information for users in the application environment .
*/
private static final Cache<String, Object> properties = CacheBuilder.newBuilder().build();
/**
* Get or Created new execute server .
* @return execute service instance of {@link ExecutorService}
*/
private static ExecutorService executor() {
if (EAR.get() == null) {
synchronized (PolarisConfigListenerContext.class) {
int coreThreadSize = Runtime.getRuntime().availableProcessors();
final ExecutorService service = new ThreadPoolExecutor(coreThreadSize, coreThreadSize,
0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(64),
new CustomizableThreadFactory("Config-Change-Notify-Thread-Pool-"));
// Register Jvm Shutdown Hook
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
LOG.info("Shutting down config change notify thread pool");
service.shutdown();
}
catch (Exception ignore) {
}
}));
EAR.set(service);
}
}
return EAR.get();
}
/**
* Initialize Environment Properties cache after listen <code>ApplicationStartedEvent</code> event .
* @param ret origin properties map
*/
static void initialize(Map<String, Object> ret) {
properties.putAll(ret);
}
/**
* Merge Changed Properties .
* @param ret current environment properties map
* @return merged properties result map
*/
static Map<String, ConfigPropertyChangeInfo> merge(Map<String, Object> ret) {
Map<String, ConfigPropertyChangeInfo> changes = Maps.newHashMap();
if (!ret.isEmpty()) {
Map<String, Object> origin = Maps.newHashMap(properties.asMap());
Map<String, ConfigPropertyChangeInfo> deleted = Maps.newHashMap();
origin.keySet().parallelStream().forEach(key -> {
if (!ret.containsKey(key)) {
deleted.put(key, new ConfigPropertyChangeInfo(key, String.valueOf(origin.get(key)), null, DELETED));
properties.invalidate(key);
}
});
changes.putAll(deleted);
ret.keySet().parallelStream().forEach(key -> {
Object oldValue = properties.getIfPresent(key);
Object newValue = ret.get(key);
if (oldValue != null) {
if (!newValue.equals(oldValue)) {
properties.put(key, newValue);
changes.put(key, new ConfigPropertyChangeInfo(key, String.valueOf(oldValue), String.valueOf(newValue), MODIFIED));
}
}
else {
properties.put(key, newValue);
changes.put(key, new ConfigPropertyChangeInfo(key, null, String.valueOf(newValue), ADDED));
}
});
}
return changes;
}
/**
* Adding a config file change listener, will trigger a callback when the config file is published .
* @param listener the listener will be added
* @param interestedKeys the keys interested in the listener, will only be notified if any of the interested keys is changed.
* @param interestedKeyPrefixes the key prefixes that the listener is interested in,
* will be notified if and only if the changed keys start with anyone of the prefixes.
*/
public static void addChangeListener(@NonNull ConfigChangeListener listener,
@Nullable Set<String> interestedKeys, @Nullable Set<String> interestedKeyPrefixes) {
if (!listeners.contains(listener)) {
listeners.add(listener);
PolarisConfigListenerContext.interestedKeys.put(listener, interestedKeys == null ? Sets.newHashSet() : interestedKeys);
PolarisConfigListenerContext.interestedKeyPrefixes.put(listener, interestedKeyPrefixes == null ? Sets.newHashSet() : interestedKeyPrefixes);
}
}
/**
* Fire config change event with {@link ConfigKVFileChangeListener} .
* @param changedKeys changed keys in listener
* @param changes target config file changes info
*/
public static void fireConfigChange(Set<String> changedKeys, Map<String, ConfigPropertyChangeInfo> changes) {
final List<ConfigChangeListener> listeners = findMatchedConfigChangeListeners(changedKeys);
for (ConfigChangeListener listener : listeners) {
Set<String> interestedChangedKeys = resolveInterestedChangedKeys(listener, changedKeys);
Map<String, ConfigPropertyChangeInfo> modifiedChanges = new HashMap<>(interestedChangedKeys.size());
interestedChangedKeys.parallelStream().forEach(key -> modifiedChanges.put(key, changes.get(key)));
ConfigChangeEvent event = new ConfigChangeEvent(modifiedChanges, interestedChangedKeys);
PolarisConfigListenerContext.executor().execute(() -> listener.onChange(event));
}
}
/**
* Try to find all matched config change listeners .
* @param changedKeys received changed keys
* @return list of matched {@link ConfigChangeListener}
*/
private static List<ConfigChangeListener> findMatchedConfigChangeListeners(Set<String> changedKeys) {
final List<ConfigChangeListener> configChangeListeners = Lists.newArrayList();
for (ConfigChangeListener listener : listeners) {
if (isConfigChangeListenerInterested(listener, changedKeys)) {
configChangeListeners.add(listener);
}
}
return configChangeListeners;
}
/**
* Check {@link ConfigChangeListener} is interested in custom keys.
* @param listener instance of {@link ConfigChangeListener}
* @param changedKeys received changed keys
* @return true is interested in custom keys
*/
private static boolean isConfigChangeListenerInterested(ConfigChangeListener listener, Set<String> changedKeys) {
Set<String> interestedKeys = PolarisConfigListenerContext.interestedKeys.get(listener);
Set<String> interestedKeyPrefixes = PolarisConfigListenerContext.interestedKeyPrefixes.get(listener);
if ((interestedKeys == null || interestedKeys.isEmpty())
&& (interestedKeyPrefixes == null || interestedKeyPrefixes.isEmpty())) {
return true;
}
if (interestedKeys != null) {
for (String interestedKey : interestedKeys) {
if (changedKeys.contains(interestedKey)) {
return true;
}
}
}
if (interestedKeyPrefixes != null) {
for (String prefix : interestedKeyPrefixes) {
for (final String changedKey : changedKeys) {
if (changedKey.startsWith(prefix)) {
return true;
}
}
}
}
return false;
}
/**
* Resolve all interested keys .
* @param listener instance of {@link ConfigChangeListener}
* @param changedKeys received changed keys
* @return set of all interested keys in listener
*/
private static Set<String> resolveInterestedChangedKeys(ConfigChangeListener listener, Set<String> changedKeys) {
Set<String> interestedChangedKeys = Sets.newHashSet();
if (interestedKeys.containsKey(listener)) {
Set<String> interestedKeys = PolarisConfigListenerContext.interestedKeys.get(listener);
for (String interestedKey : interestedKeys) {
if (changedKeys.contains(interestedKey)) {
interestedChangedKeys.add(interestedKey);
}
}
}
if (interestedKeyPrefixes.containsKey(listener)) {
Set<String> interestedKeyPrefixes = PolarisConfigListenerContext.interestedKeyPrefixes.get(listener);
for (String interestedKeyPrefix : interestedKeyPrefixes) {
for (String changedKey : changedKeys) {
if (changedKey.startsWith(interestedKeyPrefix)) {
interestedChangedKeys.add(changedKey);
}
}
}
}
return Collections.unmodifiableSet(interestedChangedKeys);
}
}

@ -0,0 +1,49 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.config.example;
import java.util.Set;
import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener;
import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent;
import org.springframework.stereotype.Component;
/**
* Custom Config Listener Example .
*
* @author Palmer Xu 2022-06-06
*/
@Component
public final class PersonConfigChangeListener {
/**
* PolarisConfigKVFileChangeListener Example .
* @param event instance of {@link ConfigChangeEvent}
*/
@PolarisConfigKVFileChangeListener(interestedKeyPrefixes = "teacher")
public void onChange(ConfigChangeEvent event) {
Set<String> changedKeys = event.changedKeys();
for (String changedKey : changedKeys) {
System.out.printf("%s = %s \n", changedKey, event.getChange(changedKey));
}
}
}
Loading…
Cancel
Save