Apollo热发布配置
一、背景
项目中需要用的阿波罗热发布,一种是利用注解ApolloConfigChangeListener的方式,可用如下配置就可。
@Component
@Slf4j
public class ApolloRefreshConfig2implements ApplicationContextAware{@Autowired
private RefreshScope refreshScope;
private ApplicationContext applicationContext;
@ApolloConfigChangeListener()
public void onChange(ConfigChangeEvent changeEvent) {
refreshProperties(changeEvent);
}private void refreshProperties(ConfigChangeEvent changeEvent) {
this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
refreshScope.refreshAll();
}@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
可是在测试中发现热部署并没有更新,后查看注解ApolloConfigChangeListener,发现如果不指定value值则默认namespace为 application。而我们项目中并没有使用application,而是拆分出来更多的命名空间;所以并不适用,需要指定value,如value=https://www.it610.com/article/{"edl-pub-service-common","Java.bmc-local-eureka","Java.bmc-pub-mysql","Java.bmc-redis","Java.bmc-rabbitmq","Java.bmc-httplog"};
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ApolloConfigChangeListener {
/**
* Apollo namespace for the config, if not specified then default to application
*/
String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
String[] interestedKeys() default {};
String[] interestedKeyPrefixes() default {};
}
这里放一下我们的配置文件
# 应用ID(在Apollo服务端新增项目添加的应用ID)
app:
id: bmc
# apollo-configservice地址
apollo:
meta: http://10.197.236.187:7070
bootstrap:
enabled: true
namespaces: edl-pub-service-common,Java.bmc-local-eureka,Java.bmc-pub-mysql,Java.bmc-redis,Java.bmc-rabbitmq,Java.bmc-httplog
eagerLoad:
enabled: true
可以不想再value中硬编码,那么问题来了:能否将value中的动态配置,比如读取配置文件中的数据?
二、尝试 1.@ApolloConfigChangeListener()注解的value直接设置EL表达式,结果------失败。
【Apollo热发布配置】如下代码设置
@ApolloConfigChangeListener(value = "https://www.it610.com/article/${apollo.bootstrap.namespacesArr}")
public void onChange(ConfigChangeEvent changeEvent) {
refreshProperties(changeEvent);
}
经过debug尝试,发现Apollo会默认注册 AutoUpdateConfigChangeListener监听器,如果扫描到ApolloConfigChangeListener注解也会注册到监听器中(源码见com.ctrip.framework.apollo.spring.annotation.ApolloAnnotationProcessor),可是在获取此注解的时候并没有判断是不是EL表达式,所以你传入 {apollo.bootstrap.namespacesArr};代码如下
@Override
protected void processMethod(final Object bean, String beanName, final Method method) {
ApolloConfigChangeListener annotation = AnnotationUtils
.findAnnotation(method, ApolloConfigChangeListener.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[] namespaces = annotation.value();
String[] annotatedInterestedKeys = annotation.interestedKeys();
String[] annotatedInterestedKeyPrefixes = annotation.interestedKeyPrefixes();
ConfigChangeListener configChangeListener = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
ReflectionUtils.invokeMethod(method, bean, changeEvent);
}
};
Set interestedKeys = annotatedInterestedKeys.length > 0 ? Sets.newHashSet(annotatedInterestedKeys) : null;
Set interestedKeyPrefixes = annotatedInterestedKeyPrefixes.length > 0 ? Sets.newHashSet(annotatedInterestedKeyPrefixes) : null;
for (String namespace : namespaces) {
Config config = ConfigService.getConfig(namespace);
if (interestedKeys == null && interestedKeyPrefixes == null) {
config.addChangeListener(configChangeListener);
} else {
config.addChangeListener(configChangeListener, interestedKeys, interestedKeyPrefixes);
}
}
}
所以该我们配置文件的属性改变因为不是指定的namespaces就不会更新。
2.利用反射动态修改@ApolloConfigChangeListener()注解的value,将value赋值为配置的属性,结果------失败。
此方法明显行不通,因为ApolloAnnotationProcessor中每次获取的ApolloConfigChangeListener 开始设置的值,而不是通过我们反射获取的值,反射的主要代码如下
try {
method = ApolloRefreshConfig.class.getMethod("onChange", ConfigChangeEvent.class);
ApolloConfigChangeListener annotation = method.getAnnotation(ApolloConfigChangeListener.class);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
Field value = https://www.it610.com/article/invocationHandler.getClass().getDeclaredField("memberValues");
value.setAccessible(true);
Map stringObjectMap = (Map) value.get(invocationHandler);
stringObjectMap.put("value", namespacesArr);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
3.新写监听器,加入监听器集合中,结果------失败。
因为始终读取不到配置文件,猜测跟ApolloProcessor 继承 BeanPostProcessor有关吧。
@Component
@Slf4j
public class ApolloRefreshListener extends ApolloProcessor implements ApplicationContextAware {@Autowired
private RefreshScope refreshScope;
@Value("${apollo.bootstrap.namespaces}")
private String namespaces;
private ApplicationContext applicationContext;
@Autowired
private ConfigPropertySourceFactory configPropertySourceFactory;
/*@PostConstruct
private void init(){
List namespacesList = Lists.newArrayList();
Splitter.on(",").omitEmptyStrings().split(namespaces).forEach(item -> namespacesList.add(item));
namespacesArr = new String[namespacesList.size()];
}*/@Override
protected void processField(Object bean, String beanName, Field field) {
ApolloConfig annotation = AnnotationUtils.getAnnotation(field, ApolloConfig.class);
if (annotation == null) {
return;
}Preconditions.checkArgument(Config.class.isAssignableFrom(field.getType()),
"Invalid type: %s for field: %s, should be Config", field.getType(), field);
String namespace = annotation.value();
Config config = ConfigService.getConfig(namespace);
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, config);
}@Override
protected void processMethod(final Object bean, String beanName, final Method method) {ApolloRefreshConfig apolloRefreshListener = new ApolloRefreshConfig();
List allConfigPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
for (ConfigPropertySource allConfigPropertySource : allConfigPropertySources) {
Config config = ConfigService.getConfig(allConfigPropertySource.getName());
config.addChangeListener(apolloRefreshListener);
}
}@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}}
三、结论 暂无解决办法,目前只有写死。
推荐阅读
- 热闹中的孤独
- 2018年6月7日|2018年6月7日 日记
- 我那水深火热的二婚生活
- 热爱的东西就得坚持哦
- 图文小编《杨浦、成毅》为你发布!无价之宝随意摆放的公园
- 惊奇于世界,还热爱于生活
- 热闹也可以,独立也可以,随时有选择的权利
- 那年的我们
- 52岁李若彤秀马甲线上热搜,凭什么啊()
- 春季试衣间|春季试衣间|UNIQLO优衣库 UR HOTWIND热风春夏新品搭配 日常搭配 可盐可甜