irpas技术客

采用 Nacos 和 Apollo 作为配置中心时@ConfigurationProperties、@RefreshScope的正确用法_浮~沉

网络 7785

心中剑-手中剑 剑起剑来剑魂

剑起

众所周知,当前各个互联网企业采用的配置中心中,无外乎 Nacos 和 Apollo 最为知名。今天不去比较优劣,因为适合自己的才是最好的,而是在两个框架出现过渡时,有些用法或者编码习惯需要调整,否则配置文件就自己单飞了。

最近因为公司技术栈过渡原因,我们从 Nacos 转为 Apollo 作为配置中心,这期间原来为了从编码规范的习惯不少类采用ConfigurationProperties注解修饰,而在接入 Apollo 配置中心后,猛然发现@RefreshScope无法自动热更新,这无疑是致命的。


剑来

下面演示Nacos 迁移到 Apollo不生效的场景

@Component @RefreshScope @ConfigurationProperties(prefix = "elasticsearch.check") public class EsCheckConfig { // 迁移Apollo 后如果不做额外的配置 手动修改值是无法被感知的 private Integer flag; }

分析: 采用 SpringCloud 原生注解@RfreshScope,迁移到 Apollo 后对自定义配置的控制权都完全丢失,所以丢失相应的功能也能理解;既然不是一个体系,也不必考虑太多。

正确的打开方式

完全之策 直接依赖 SpringBoot 原生的@Value 注解,而这注定有几个问题 1. 虽然同样解决了问题,好像大家都知道的完全之策,更像是废话 2. 可偏执的我们总想知道@ConfigurationProperties 这家伙能不能热更新。 3. 如果像我们在已经搭建的体系下,去使用@Value方式,必然耗费一番功夫,而且几乎需要走一遍所有用例,确保迁移正确。 他山之石-稳定版 import com.ctrip.framework.apollo.model.ConfigChange; import com.ctrip.framework.apollo.model.ConfigChangeEvent; import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.context.scope.refresh.RefreshScope; import org.springframework.stereotype.Component; import java.util.Set; @Slf4j @Component public class ApolloConfig { private final RefreshScope refreshScope; private final EsCheckConfig esCheckConfig; public ApolloConfig(RefreshScope refreshScope, EsCheckConfig esCheckConfig) { this.refreshScope = refreshScope; this.esCheckConfig = esCheckConfig; } @ApolloConfigChangeListener private void someOnChange(ConfigChangeEvent changeEvent) { // 这部分是为了解决ConfigurationProperties 无法获取 apollo 变动的日志 Set<String> changedKeys = changeEvent.changedKeys(); changedKeys.forEach(k -> { ConfigChange change = changeEvent.getChange(k); log.info(change.toString()); }); // apollo 自带的热刷新 refreshScope.refresh("esCheckConfig"); // refreshScope.refreshAll(); } }

这下虽然满足了诉求,而这又带了几个问题【以下是戏精环节】

这 TMD 太离谱了,我那么多ConfigurationProperties 修饰的类,都要注入一遍?好,Apollo 还算良心,看到上面的注释了嘛,有了 refreshAll() 好像再也不需要注入所有的类,于是长呼一口气,等等 我草这玩意儿是不是没更新都会刷新一遍,Yes!但凡大中型企业的配置,没有上万也有上千。还有个问题,这只能解决你当下服务的配置,那么多微服务如果不依赖一个公共的底层服务去处理,那也是掩耳盗铃。

哦,对了上面还有简陋版

区别在于refreshScope.refresh("esCheckConfig")底层也是使用了this.applicationContext.publishEvent 不同的是在 spring 的发布事件基础上加锁了,Apollo 开发者也是细腻和全面,不过有谁一天无聊的操作同一个配置呢?如果只有几个人维护的话用下面倒也未尝不可

public class ApolloConfig implements ApplicationContextAware { private ApplicationContext applicationContext; @ApolloConfigChangeListener private void someOnChange(ConfigChangeEvent changeEvent) { Set<String> changedKeys = changeEvent.changedKeys(); changedKeys.forEach(k -> { ConfigChange change = changeEvent.getChange(k); log.info(change.toString()); }); // 更新@ConfigurationProperties注解的bean this.applicationContext.publishEvent(new EnvironmentChangeEvent(changedKeys)); } }

所以,实际开发中该怎么做?笔者探寻一圈,难以两全,所以有了下文


剑魂

使用@Value一定没毛病的老铁哈哈哈,所以在你编码习惯上就应该有这样的意识,如果还在使用@ConfigurationProperties的老铁还是尽早悬崖勒马,否则早晚要陷入我等这样的困境,实在是想用,就要实时记得用apollo 自带的热更新去检查

当然了,如果像Redis、MySQL 的这样固定的配置,@ConfigurationProperties也是没毛病,当你要修改这些基础配置时,重启在所难免, 遗憾的是 SpringBoot 都集成了

还有,细心的童鞋应该还发现上面一段记录ConfigChangeEvent打印变更key 的先后信息,这个在使用@ConfigurationProperties避免带来的隐患,该注解修饰下的对象被 spring代理。

因此当你发生值变更的时候,what 你啥也感知不到,好像啥也没发生,这样的事情你允许在生产环境吗?所以使用@value吧,直到 SpringBoot 也能自带支持@ConfigurationProperties热更新那一天!


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #采用 #nacos # #apollo