源码级分析Spring Cloud Config这篇文章讲过Spring Cloud Config如何在应用启动时加载远程配置,但Spring Cloud Config更令人着迷的是它的动态刷新机制,应用可以在不重启的情况下实现应用配置的在上下文中被更新,我们称之为应用配置的动态刷新。
Spring Cloud Config数据流
我们通常采用两种方式动态的刷新应用配置,一种是让应用主动的拉取,另外一种是Config server端主动推送到应用端,我比较倾向于后者。因为应用主动拉取的话服务端很难区分哪些配置需要更新,哪些不需要更新,只有把新的配置内容全量的返回给应用,由应用端来刷新所有的应用配置。但是如果是服务端主动推送的,那么服务端就可以选择推送变更的配置,而应用端可以自定义配置刷新的处理器只刷新使用了更新的配置的Bean。影响范围会相对较小,避免一些因为动态刷新造成的副作用。
两种更新配置方式
这里我们设计服务端通过消息队列推送更新的配置到应用端,应用端消费消息来刷新配置,我们甚至可以利用消息队列的特性(比如RoecktMQ)通过消息的topci和tag来使刷新配置的维度粒度更细致,比如是刷新整个集群还是刷新某个节点,或者刷新部分节点。不过这个不是本文的重点,大家可以思考下这个设计思路。本文主要介绍应用端是如何自定义处理消息然后动态刷新配置的。
Spring Cloud Config框架的自带的刷新配置功能会在调用Refresh endpint期间刷新应用程序的应用上下文,这将停止当前应用程序包括正在运行的线程。
packageorg.springframework.cloud.endpoint;importjava.util.Collection;importjava.util.Set;importorg.springframework.boot.actuate.endpoint.annotation.Endpoint;importorg.springframework.boot.actuate.endpoint.annotation.WriteOperation;importorg.springframework.cloud.context.refresh.ContextRefresher;@Endpoint(
id ="refresh")publicclassRefreshEndpoint{privateContextRefresher contextRefresher;publicRefreshEndpoint(ContextRefresher contextRefresher) {this.contextRefresher = contextRefresher;
}@WriteOperationpublicCollection refresh() {// 调用refresh接口会触发动态刷新配置Set keys =this.contextRefresher.refresh();returnkeys;
}
}
我们看到refresh方法里ContextRefresher的refresh()会执行应用上下文的刷新
public synchronizedSet<String> refresh() {Set<String> keys =this.refreshEnvironment();this.scope.refreshAll();returnkeys;
}
我们接着进一步看refresh方法里会调用的refreshEnvironment(),我们可以看到它会重新构建整个ApplicationContext,相当于一个应用重启,这样的动态刷新其实还是比较重的,即使我只改了一条配置都需要重启整个应用的上下文。
我们接着进一步看refresh方法里会调用的refreshEnvironment(),我们可以看到它会重新构建整个ApplicationContext,相当于一个应用重启,这样的动态刷新其实还是比较重的,即使我只改了一条配置都需要重启整个应用的上下文。
public synchronizedSet<String> refreshEnvironment() {Map<String,Object> before =this.extract(this.context.getEnvironment().getPropertySources());this.addConfigFilesToEnvironment();Set<String> keys =this.changes(before,this.extract(this.context.getEnvironment().getPropertySources())).keySet();this.context.publishEvent(newEnvironmentChangeEvent(this.context, keys));returnkeys;
}
ConfigurableApplicationContext addConfigFilesToEnvironment() {
ConfigurableApplicationContext capture =null;
boolean var15 =false;try{
var15 =true;
StandardEnvironment environment =this.copyEnvironment(this.context.getEnvironment());// 创建一个新的 spring application builderSpringApplicationBuilder builder = (newSpringApplicationBuilder(newClass[]{ContextRefresher.Empty.class})).bannerMode(Mode.OFF).web(WebApplicationType.NONE).environment(environment);
builder.application().setListeners(Arrays.asList(newBootstrapApplicationListener(),newConfigFileApplicationListener()));//通过SpringApplicationBuild重新构建applicationcapture = builder.run(newString[0]);if(environment.getPropertySources().contains("refreshArgs")) {
environment.getPropertySources().remove("refreshArgs");
}
MutablePropertySources target =this.context.getEnvironment().getPropertySources();StringtargetName =null;
Iterator var6 = environment.getPropertySources().iterator();
所以我们只提取spring cloud的部分刷新功能,只在配置属性更新后重建bean,而不去重启整个应用上下文。假如我们是通过消费消息队列来处理动态刷新,那么我们可以在消息的Listener里添加自定义的处理器来实现这一点
protectedvoidhandleConfigChangeMessage(MessageExt messageExt)throwsInterruptedException, RemotingException, MQClientException, MQBrokerException{
refreshConfig(messageExt);//只执行refreshScope.refreshAll() 方法,去除refreshEnvironment这一步,忘记在Listener里注入RefreshScoperefreshScope.refreshAll();
}privatevoidregreshConfig(){//解析Message成键值对//通过EnvironmentManager 更新上下文中的配置内容,别忘记在Listener里注入EnvironmentManeger}
RefreshScode的refreshAll方法会扫描项目中加了@RefreshScope的bean,然后重新构建bean,在构建过程中由于前一步已经把Environment中的配置更新了,所以构建的bean使用的就是新的配置了。
六不六,感觉六的就素质三连吧(点赞,收藏,转发)。不过这里其实有个副作用,篇幅有限,先卖个关子吧,没时间了,还得去喂狗。下次更新下这里面有什么坑,欢迎继续关注。
声明:本文部分素材转载自互联网,如有侵权立即删除 。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别
丞旭猿论坛
暂无评论内容