您好,登錄后才能下訂單哦!
這篇文章主要介紹了apollo怎么更改配置刷新@ConfigurationProperties配置類的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇apollo怎么更改配置刷新@ConfigurationProperties配置類文章都會有所收獲,下面我們一起來看看吧。
apollo配置經常使用的方式是@value,比較便捷,如果只出現在一個類中還行,但是如果多個類中并不是很方便,特別是如果出現配置值變化了之后需要觸發相關變動也無法實現,因此就會考慮使用配置類@ConfigurationProperties,它能實現:
統一管理一組配置。
配置變化的時需要觸發相關變動只涉及一個bean。
但是apollo配置變化時不會把@ConfigurationProperties的值進行更新,具體看官方文檔,需要配合EnvironmentChangeEvent或RefreshScope使用。
可以大概看看:
package com.ctrip.framework.apollo.use.cases.spring.cloud.zuul; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent; import org.springframework.cloud.netflix.zuul.filters.RouteLocator; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ZuulPropertiesRefresher implements ApplicationContextAware { private static final Logger logger = LoggerFactory.getLogger(ZuulPropertiesRefresher.class); private ApplicationContext applicationContext; @Autowired private RouteLocator routeLocator; @ApolloConfigChangeListener(interestedKeyPrefixes = "zuul.") public void onChange(ConfigChangeEvent changeEvent) { refreshZuulProperties(changeEvent); } private void refreshZuulProperties(ConfigChangeEvent changeEvent) { logger.info("Refreshing zuul properties!"); /** * rebind configuration beans, e.g. ZuulProperties * @see org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#onApplicationEvent */ this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys())); /** * refresh routes * @see org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration.ZuulRefreshListener#onApplicationEvent */ this.applicationContext.publishEvent(new RoutesRefreshedEvent(routeLocator)); logger.info("Zuul properties refreshed!"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
使用@ApolloConfigChangeListener注冊了apollo配置變化監聽器,內部使用了cloud發布EnvironmentChangeEvent事件進行更新。
/* * Copyright 2022 Apollo Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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.apolloconfig.apollo.demo.springboot.refresh; import com.apolloconfig.apollo.demo.springboot.config.SampleRedisConfig; import com.ctrip.framework.apollo.core.ConfigConsts; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.stereotype.Component; @ConditionalOnProperty("redis.cache.enabled") @Component public class SpringBootApolloRefreshConfig { private static final Logger logger = LoggerFactory.getLogger(SpringBootApolloRefreshConfig.class); private final SampleRedisConfig sampleRedisConfig; private final RefreshScope refreshScope; public SpringBootApolloRefreshConfig( final SampleRedisConfig sampleRedisConfig, final RefreshScope refreshScope) { this.sampleRedisConfig = sampleRedisConfig; this.refreshScope = refreshScope; } @ApolloConfigChangeListener(value = "${listeners}", interestedKeyPrefixes = {"redis.cache."}) public void onChange(ConfigChangeEvent changeEvent) { logger.info("before refresh {}", sampleRedisConfig.toString()); refreshScope.refresh("sampleRedisConfig"); logger.info("after refresh {}", sampleRedisConfig); } }
/* * Copyright 2022 Apollo Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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.apolloconfig.apollo.demo.springboot.config; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Component; /** * You may set up data like the following in Apollo: <br /><br /> Properties Sample: * application.properties * <pre> * redis.cache.enabled = true * redis.cache.expireSeconds = 100 * redis.cache.clusterNodes = 1,2 * redis.cache.commandTimeout = 50 * redis.cache.someMap.key1 = a * redis.cache.someMap.key2 = b * redis.cache.someList[0] = c * redis.cache.someList[1] = d * </pre> * * Yaml Sample: application.yaml * <pre> * redis: * cache: * enabled: true * expireSeconds: 100 * clusterNodes: 1,2 * commandTimeout: 50 * someMap: * key1: a * key2: b * someList: * - c * - d * </pre> * * To make <code>@ConditionalOnProperty</code> work properly, <code>apollo.bootstrap.enabled</code> * should be set to true and <code>redis.cache.enabled</code> should also be set to true. Check * 'src/main/resources/application.yml' for more information. * */ @ConditionalOnProperty("redis.cache.enabled") @ConfigurationProperties(prefix = "redis.cache") @Component("sampleRedisConfig") @RefreshScope public class SampleRedisConfig implements InitializingBean { private static final Logger logger = LoggerFactory.getLogger(SampleRedisConfig.class); private int expireSeconds; private String clusterNodes; private int commandTimeout; private Map<String, String> someMap = Maps.newLinkedHashMap(); private List<String> someList = Lists.newLinkedList(); @Override public void afterPropertiesSet() throws Exception { logger.info( "SampleRedisConfig initialized - expireSeconds: {}, clusterNodes: {}, commandTimeout: {}, someMap: {}, someList: {}", expireSeconds, clusterNodes, commandTimeout, someMap, someList); } public void setExpireSeconds(int expireSeconds) { this.expireSeconds = expireSeconds; } public void setClusterNodes(String clusterNodes) { this.clusterNodes = clusterNodes; } public void setCommandTimeout(int commandTimeout) { this.commandTimeout = commandTimeout; } public Map<String, String> getSomeMap() { return someMap; } public List<String> getSomeList() { return someList; } @Override public String toString() { return String.format( "[SampleRedisConfig] expireSeconds: %d, clusterNodes: %s, commandTimeout: %d, someMap: %s, someList: %s", expireSeconds, clusterNodes, commandTimeout, someMap, someList); } }
使用了RefreshScope進行刷新配置(重新create bean),但是不夠高效,最理想的事一個值發生變化只要重新把對應的屬性set即可。
那么我們下面嘗試下,需要解決問題:
確定@ApolloConfigChangeListener.value能不能填*
表示匹配所有namespace。
根據ConfigChangeEvent找到配置類及對應的成員進行set。
@ApolloConfigChangeListener(value = "*") public void onChange(ConfigChangeEvent changeEvent) { log.info("onChange changeEvent:{}", changeEvent); }
發現并不行。應該可以編程式添加listener
找到官方文檔:
Config config = ConfigService.getAppConfig(); config.addChangeListener(new ConfigChangeListener() { @Override public void onChange(ConfigChangeEvent changeEvent) { for (String key : changeEvent.changedKeys()) { ConfigChange change = changeEvent.getChange(key); System.out.println(String.format( "Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType())); } } });
ConfigService.getAppConfig()
默認監聽application
namespace,我們需要只監聽項目依賴的的namespace。
google了一下發現:apollo配置中心之--spring boot如何加載apollo。 可以通過environment獲取key為ApolloPropertySources
,我們嘗試下:
@PostConstruct public void registerApolloConfigChangeListener() { //從env中拿到所有已從Apollo加載的propertySource,獲取監聽的nameSpace CompositePropertySource apolloPropertySources = (CompositePropertySource) configurableEnvironment.getPropertySources().get("ApolloPropertySources"); if (Objects.isNull(apolloPropertySources)) { return; } Collection<PropertySource<?>> propertySourceList = apolloPropertySources.getPropertySources(); //注冊監聽所有加載的nameSpace propertySourceList.forEach(propertySource -> { ConfigChangeListener configChangeListener = changeEvent -> { for (String changedKey : changeEvent.changedKeys()) { ConfigChange change = changeEvent.getChange(changedKey); System.out.println(String.format( "Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType())); } }; Config config = ConfigService.getConfig(propertySource.getName()); config.addChangeListener(configChangeListener); }); }
google了一下,沒找到,但是spring肯定有相關的實現,比如讀取yml后對 @ConfigurationProperties
屬性進行填充,通過這篇文章找到ConfigurationPropertiesBindingPostProcessor是核心處理類:
通過ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization進行賦值,下面我們來看看能不能直接使用它:
package com.onepiece.apollo; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigChangeListener; import com.ctrip.framework.apollo.ConfigService; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertySource; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.Collection; import java.util.Comparator; import java.util.Map; import java.util.Objects; import java.util.Optional; @Slf4j @Component public class ApolloRefreshConfig implements BeanPostProcessor, Ordered { @Resource private ConfigurableEnvironment configurableEnvironment; private Map<String, String> configPrefixBeanNameMapping; @Resource private ConfigurationPropertiesBindingPostProcessor configurationPropertiesBindingPostProcessor; @Resource private ApplicationContext applicationContext; /** * 注冊configChangeListener監聽指定的NameSpace,默認的業務配置都在與應用名命名的nameSpace,當然了如果希望監聽到更多的自己去拿到配置的nameSpace也可以的 */ @PostConstruct public void registerApolloConfigChangeListener() { //從env中拿到所有已從Apollo加載的propertySource,獲取監聽的nameSpace CompositePropertySource apolloPropertySources = (CompositePropertySource) configurableEnvironment.getPropertySources().get("ApolloPropertySources"); if (Objects.isNull(apolloPropertySources)) { return; } Collection<PropertySource<?>> propertySourceList = apolloPropertySources.getPropertySources(); //注冊監聽所有加載的nameSpace propertySourceList.forEach(propertySource -> { ConfigChangeListener configChangeListener = changeEvent -> { for (String changedKey : changeEvent.changedKeys()) { log.info("apollo changed namespace:{} Key:{} value:{}", changeEvent.getNamespace(), changedKey, changeEvent.getChange(changedKey)); String beanName = getBeanName(changedKey); configurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(applicationContext.getBean(beanName), beanName); } }; Config config = ConfigService.getConfig(propertySource.getName()); config.addChangeListener(configChangeListener); }); } /** * register beanName with ConfigurationProperties#prefix if * annotation @ConfigurationProperties and @RefreshScope is existed * * @param bean * @param beanName * @return * @throws BeansException */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { ConfigurationProperties propertiesAnno = bean.getClass().getAnnotation(ConfigurationProperties.class); if (propertiesAnno != null) { if (configPrefixBeanNameMapping == null) { configPrefixBeanNameMapping = Maps.newHashMap(); } String prefix = propertiesAnno.prefix() != null ? propertiesAnno.prefix() : propertiesAnno.value() == null ? null : propertiesAnno.value(); if (StringUtils.isNotBlank(prefix) && StringUtils.isNotBlank(beanName)) { this.configPrefixBeanNameMapping.put(prefix, beanName); } } return bean; } @Override public int getOrder() { return 0; } /** * 防止可能出現的匹配到短prefix的情況,例如 key = "a.ab.abc", prefixA = "a", prefixB = "a.ab.abc",匹配到prefixA返回的情況,這里需要得到最匹配 * * @param key * @return beanName best match key */ private String getBeanName(String key) { if (configPrefixBeanNameMapping != null) { Optional<Map.Entry<String, String>> bestMatchEntry = configPrefixBeanNameMapping.entrySet().stream() .filter(entryt -> key.startsWith(entryt.getKey() + ".")) .max(Comparator.comparing(Map.Entry<String, String>::getKey)); return bestMatchEntry.map(Map.Entry::getValue).orElse(null); } return null; } }
關于“apollo怎么更改配置刷新@ConfigurationProperties配置類”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“apollo怎么更改配置刷新@ConfigurationProperties配置類”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。