需求
GitHub地址:https://github.com/ctripcorp/apollo/wiki/Apollo配置中心介绍#32-界面概览
实际上大多数人的文章都是copy的这个,没有任何参考意义. 没有一点自己的东西.
文章说只支持Spring3.1.1以上. 由于我们项目比较老. 用的还是Spring3.0.5,现在项目需要集成分布式配置中心,之前考虑过Spring Cloud Config,发现旧的Spring版本集成有很大的冲突.不太适合. 所以考虑选择携程Apollo.
话不多说. 上代码.
2.1 引入依赖
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.4.0</version>
</dependency>
2.2 新建配置文件
resource目录下新建目录META-INF,然后新建配置文件app.properties
apollo.meta=http://192.168.13.34:8080
env=DEV
app.id=demo-module
apollo.bootstrap.enabled=true
注: 这些参数都可以放到JVM启动参数里. 后续用Docker部署会使用到.
获取配置的方式,除了SpringBoot的注解方式,还有ConfigService.getConfig()
Config apolloConfig = ConfigService.getAppConfig();
String value = apolloConfig.getProperty("key", "defaultValue");
实际上Apollo除了配置属性,还有实时更新,以及灰度发布功能,这个才是亮点. 下面就讲讲我的踩坑记录.
集成完上面之后启动工程.OK,获取到配置,动态获取OK,完美. 但是如何做到配置修改动态更新?
查看github的帮助文档.直接增加一个listener即可.
扩展下Apollo动态更新配置的原理:线程池里启动一个Http的长连接,每个实例都有. 如果配置发生了变化,则与本地的配置的文件作比较. 然后更新到JVM里.
代码如下:
// 获取阿波罗的配置信息
Config apolloConfig = ConfigService.getAppConfig();
// 注册参数变更监听
apolloConfig.addChangeListener(new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent configChangeEvent) {
for (String key : configChangeEvent.changedKeys()) {
ConfigChange change = configChangeEvent.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()));
System.setProperty(String.valueOf(key), String.valueOf(change.getNewValue()));
}
}
});
那么现在在界面上修改配置,并且发布之后,本地理论上就可以收到变更.
再扩展下是如何实现的:Apollo分为三个模块. Portal负责界面的操作. AdminService负责与Portal交互,做一些配置的增删改查,ConfigService负责与客户端通信,负责推送和获取配置.当配置发送变更后, Portal点击发布后,AdminService会发布一个消息给ConfigService
c.c.f.a.b.message.DatabaseMessageSender : Sending message appId+default+application to channel apollo-release, 然后客户端通过轮询的方式来获取ConfigService获取最新的配置.
然而事实总是很残酷.按照官方的配置后,果然没有自动更新.果断看源码是如何实现的.
至于具体的调试步骤不详细写了。
最后定位到是DefaultConfig.updateAndCalcConfigChanges方法里的170行有问题
注意看这里的setNewValue,实际上chane里已经包含了新值和旧值,不清楚作者为何还要去通过getProperty再次获取.
问题就在于这个step1, 如果工程启动的时候我将所有属性全部加入到System中之后,如果再有变更,这个方法里永远返回的是旧值. 实际上此时的step2里的m_configProperties里已经是新值了. 所以就覆盖了change对象里的newValue,导致和oldValue一致了。然后后面判断即没有变化,自然就不会调用我们的listener了.
针对此问题已经向宋大佬提问了。还未回复
Github的提问:https://github.com/ctripcorp/apollo/issues/2501
个人理解这里实际上不需要再去getProperty的。直接使用change对象里的。
本地修改源码后.去掉
然后再启动测试参数实时更新正常.