springboot + 携程apllo 配置实时生效(1)-指定生效
springboot + 携程apllo 配置实时生效(2)-全局生效
公司使用的配置中心框架,为携程开源的Apollo。但是在使用过程中,在apollo 上配置一直不能实时生效,需要重新启动服务才可以。初步参看了一些博客,发现归根结底还是要回归到Apollo的官方文档上,官方文档和说明也还是相当的详细。
附: apollo wiki: https://github.com/ctripcorp/apollo/wiki
先附上直接验证成功的demo
配置一个监听器,apollo 更新配置后,会实时监听并更新config
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigFile;
import com.ctrip.framework.apollo.ConfigFileChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.internals.YamlConfigFile;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.model.ConfigFileChangeEvent;
import com.ctrip.framework.foundation.Foundation;
import com.google.common.base.Charsets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ApolloAutoModifyConfig {
private static final Logger logger = LoggerFactory.getLogger(ApolloAutoModifyConfig.class);
private String DEFAULT_VALUE = "";
private Config config;
private Config yamlConfig;
private Config publicConfig;
private ConfigFile applicationConfigFile;
private ConfigFile xmlConfigFile;
private YamlConfigFile yamlConfigFile;
private RefreshScope refreshScope;
public static ApolloAutoModifyConfig getInstance;
static {
getInstance = new ApolloAutoModifyConfig();
}
private ApolloAutoModifyConfig() {
ConfigChangeListener changeListener = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
logger.info("Changes-111111 for namespace {}", changeEvent.getNamespace());
for (String key : changeEvent.changedKeys()) {
ConfigChange change = changeEvent.getChange(key);
logger.info("Change-11111 - key: {}, oldValue: {}, newValue: {}, changeType: {}",
change.getPropertyName(), change.getOldValue(), change.getNewValue(),
change.getChangeType());
}
}
};
ConfigFileChangeListener listener = new ConfigFileChangeListener() {
@Override
public void onChange(ConfigFileChangeEvent changeEvent) {
logger.info("Changes-22222 for namespace {}", changeEvent.getNamespace());
}
};
config = ConfigService.getAppConfig();
config.addChangeListener(changeListener);
yamlConfig = ConfigService.getConfig("application.yaml");
yamlConfig.addChangeListener(changeListener);
//监听其他公共的namespace
/*publicConfig = ConfigService.getConfig("比如引用的其他公共配置");
publicConfig.addChangeListener(changeListener);*/
applicationConfigFile = ConfigService.getConfigFile("application", ConfigFileFormat.Properties);
applicationConfigFile.addChangeListener(listener);
// datasources.xml
xmlConfigFile = ConfigService.getConfigFile("datasources", ConfigFileFormat.XML);
xmlConfigFile.addChangeListener(new ConfigFileChangeListener() {
@Override
public void onChange(ConfigFileChangeEvent changeEvent) {
logger.info(changeEvent.toString());
}
});
// application.yaml
yamlConfigFile = (YamlConfigFile) ConfigService.getConfigFile("application", ConfigFileFormat.YAML);
}
public String getConfig(String key) {
String result = config.getProperty(key, DEFAULT_VALUE);
if (DEFAULT_VALUE.equals(result)) {
result = publicConfig.getProperty(key, DEFAULT_VALUE);
}
if (DEFAULT_VALUE.equals(result)) {
result = yamlConfig.getProperty(key, DEFAULT_VALUE);
}
logger.info(String.format("Loading key : %s with value: %s", key, result));
return result;
}
private void print(String namespace) {
switch (namespace) {
case "application":
print(applicationConfigFile);
return;
case "xml":
print(xmlConfigFile);
return;
case "yaml":
printYaml(yamlConfigFile);
return;
}
}
private void print(ConfigFile configFile) {
if (!configFile.hasContent()) {
System.out.println("No config file content found for " + configFile.getNamespace());
return;
}
System.out.println("=== Config File Content for " + configFile.getNamespace() + " is as follows: ");
System.out.println(configFile.getContent());
}
private void printYaml(YamlConfigFile configFile) {
System.out.println("=== Properties for " + configFile.getNamespace() + " is as follows: ");
System.out.println(configFile.getConfigFileFormat());
}
private void printEnvInfo() {
String message = String.format("AppId: %s, Env: %s, DC: %s, IP: %s", Foundation.app()
.getAppId(), Foundation.server().getEnvType(), Foundation.server().getDataCenter(),
Foundation.net().getHostAddress());
System.out.println(message);
}
public static void main(String[] args) throws IOException {
ApolloAutoModifyConfig apolloConfigDemo = new ApolloAutoModifyConfig();
apolloConfigDemo.printEnvInfo();
System.out.println(
"Apollo Config Demo. Please input key to get the value.");
while (true) {
System.out.print("> ");
String input = new BufferedReader(new InputStreamReader(System.in, Charsets.UTF_8)).readLine();
if (input == null || input.length() == 0) {
continue;
}
input = input.trim();
try {
if (input.equalsIgnoreCase("application")) {
apolloConfigDemo.print("application");
continue;
}
if (input.equalsIgnoreCase("xml")) {
apolloConfigDemo.print("xml");
continue;
}
if (input.equalsIgnoreCase("yaml") || input.equalsIgnoreCase("yml")) {
apolloConfigDemo.print("yaml");
continue;
}
if (input.equalsIgnoreCase("quit")) {
System.exit(0);
}
apolloConfigDemo.getConfig(input);
} catch (Throwable ex) {
logger.error("some error occurred", ex);
}
}
}
}
配置使用:
@Slf4j
@Component
@Data
public class ApolloBusinessConfig {
private String mytest;
//my.test: 为apollo 配置的key
public String getMyTest() {
mytest = ApolloAutoModifyConfig.getInstance.getConfig("my.test");
return mytest;
}
}
//在业务场景使用:
@Autowired
ApolloBusinessConfig config;
String myTest = config.getMyTest();
myTest获取的结果为实时更新后的值
以上方式,只能使用ApolloBusinessConfig 的getXX 方法获取最新值。如果业务中,在其他地方使用了下面的方式获取值,获取的值还是之前的,并不会实时更新。
@Value("${my.test}")
private String myTest;
总结:
ApolloAutoModifyConfig 的监听,能实时更新缓存到本地或服务端的缓存文件配置,也能实时更新
config = ConfigService.getAppConfig();
config 在内存中的key 值,但是无法更新所有其他地方 注入@Value 或者在其他位置直接使用的key 值,综合来说,即对于 my.test在内存中的其他scop 没有刷新。查看官方文档,说是使用 EnvironmentChangeEvent或RefreshScope 进行刷新,但是测试后,并没有效果,不确定是不是和我们使用的springboot 2.0以下版本有关。后续在细作研究,看看怎么实现更新
开发语言使用的是java 并感兴趣的同学,可以直接跳转到java客户端的开发文档 参考
文档摘取:
3.1.2 监听配置变化事件
Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null
config.addChangeListener(new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
System.out.println("Changes for namespace " + changeEvent.getNamespace());
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()));
}
}
});
//获取实时更新的value
String yourValue = config.getProperty("your key","your default value");
同时文档介绍了 基于javaBean、spring xml 、springboot 的配置使用,但是真正说实时生效的是要参考源码的demo 示例:
需要注意的是,@ConfigurationProperties如果需要在Apollo配置变化时自动更新注入的值,需要配合使用EnvironmentChangeEvent或RefreshScope。相关代码实现,可以参考apollo-use-cases项目中的ZuulPropertiesRefresher.java和apollo-demo项目中的SampleRedisConfig.java以及SpringBootApolloRefreshConfig.java
根据上面源码中的示例,试验了