在微服务架构中,配置中心是必不可少的基础服务。ConfigKeeper已开源,本文将深度分析配置中心的核心内容,错过「Spring Cloud中国社区北京沙龙-2018.10.28 」的同窗将从本篇文章中收获现场的分享内容。
微服务+容器架构后,为了方便动态更新应用配置,须要把配置文件放到应用执行包以外的配置中心,这样一来,一个可执行包就能够在不一样的环境下运行,大幅度下降包的版本管理成本,也能够有效控制docker镜像的版本管理成本。传统的经过配置文件、数据库等方式已经愈来愈没法知足开发人员对配置管理的需求。对程序配置的指望值也愈来愈高:配置修改后实时生效,分环境、分集群管理配置,完善的权限、审核机制等等。因而便诞生了ConfigKeeper。html
ConfigKeeper是随行付架构部基于Spring Cloud研发的分布式配置中心,与Spring Boot、Spring Cloud应用无缝兼容。
虽然Spring Cloud 已经为咱们提供了基于git或mongodb等实现的配置中心,可是这些方案实现都过于简单,没有达到实际可用的标准。好比:没有提供统一的管理页面,不便于操做和使用;没有权限管理功能;没有数据验证功能等等。但Spring Cloud Config的核心技术仍是能够为咱们所用,没有必要从新造轮子。前端
市面上已经有几款比较成型的配置中心,你们耳熟能详的携程Apollo和百度Disconf,而咱们的配置中心底层是基于Spring Cloud Config模块进行扩展的,首先来看看Apollo、Spring Cloud Config、ConfigKeeper的功能差别:java
功能点 | Apollo | Spring Cloud Config | ConfigKeeper | |
---|---|---|---|---|
配置界面 | 一个界面管理不一样环境、不一样集群配置 | 无,须要经过git操做 | 配置信息落入数据库中,友好页面管理 | |
配置生效时间 | 实时 | 重启生效或者手动refresh生效 | 实时推送、重启生效、手动refresh生效 | |
版本管理 | 界面上直接提供发布历史和回滚按钮 | 无,须要经过git操做 | 管理页面一键回滚 | |
灰度发布 | 支持 | 不支持 | 支持,与Spring Cloud其余组件打通 | |
受权、审核、审计 | 界面上直接支持,并且支持修改、发布权限分离 | 须要经过git仓库设置,且不支持修改、发布权限分离 | 应用分配制权限管理 | |
实例配置监控 | 能够方便的看到当前哪些客户端在使用哪些配置 | 不支持 | 心跳推送,一目了然 | |
配置获取性能 | 快,经过数据库访问,还有缓存支持 | 较慢,须要从git clone repository,而后从文件系统读取 | 本地式缓存文件,配置增量推送 | |
客户端支持 | 原生支持全部Java和.Net应用 | 支持Spring应用,提供annotation获取配置 | Spring、Spring Boot、Spring Cloud | |
支持YAML格式 | 不支持 | 支持 | 支持 |
除了上述以外,还有如下其余功能特性:nginx
有史以来最简单的配置中心。使用数据库保存配置是由于微服务拆分粒度相对比较细,使用的配置也会相对比较少,因此使用数据库表就够保存,流程以下:git
经过讲解管理后台功能,理解咱们当初出于什么缘由为何要这么设计?能解决哪些问题?设计时的考虑点有哪些?经过前面的阅阅读,已知ConfigKeeper有如下核心功能:
github
为何要有权限管理?spring
这个权限系统是咱们最初设计的,咱们内部如今使用了一个统一的权限系统。为了下降管理成本,咱们也开发了微服务管理平台,将配置中心,注册中心,网关管理后台等一系列基础服务都接入到此平台来管理,并经过此平台统一进行权限管理;mongodb
咱们使用开源系统越多,那么须要管理的帐号就会越多,若是团队比较大的话,会增长很是大的管理成本。docker
配置中心的部署比较灵活,支持多环境集中式管理。可是随行付内部,为了隔离生产环境,咱们分开部署了两套配置中心,一套负责开发环境、测试环境、准生产环境的配置管理,另外一套负责生产环境的配置管理。固然开发工程师能够选择使用本地配置,不强制开发者环境与配置中心强关联。(只要考虑开发人员众多,需求同步进行)数据库
先回想一下:你有使用jar将配置共享给别人,或别人将提供给你带配置的jar?答案是确定的,这应该是开发中必须面对的问题,那么使用jar共享配置会带来哪些问题呢?
以前为了统一日志的输出格式,将logback.xml打成一个jar里,让你们使用;而我去年在推新的logback配置规范时,发现与它发生冲突了。为了解决这个冲突,咱们在每一个项目中增长了个空的logbak.xml文件。
须要与jar包提供方进行协调,还要确认修改是否对其它应用产生影响。
好比有些项目为了复用数据库操做部分代码,将数据库操做以及配置都放到单独的模块,以jar的形式进行复用,若是从复用的角度来看,是很是不错的方法。
可是当系统发展到必定程度后,有些应用的并发量上来了,其数据库链接池的配置就要与其它应用有差异,这时咱们仍是须要将配置今后模块中拆出来。
经过上面的例子,能够发现配置之因此从代码中提取出,其核心做用就是为了更好适应变化。由于共享配置存在以这些问题,并且微服务架构下,尽可能仍是以服务的方式来复用业务功能。再者咱们一直要将代码进行解偶,那么配置更须要进行解偶。
出于以上种种缘由考虑,咱们在设计配置中心时,也就没有考虑设计以“组”的形式来共享配置。这也是咱们设计时争议比较大的地方。
分为应用配置和全局配置:
为何还要全局配置?这遇前面讲的组共享配置不是冲突了吗?
全局配置只是用于适应运行环境的变化而设计的,不设计到业务配置。“组”的界限不是很清楚,很容易乱,而全局配置不存在这方面的问题。
为何单个应用只支持单个配置?
微服务已经拆得比较小了,其配置内容也不会很是多,因此只设计为一个应用只有一个配置。并且通过咱们的实践呢,一个配置是能够知足实际须要的。
咱们的版本设计相比Git的,要比较简单,可是相应的功能也还有的。主要职责以下:
无论是在内部推广时,仍是开源后,都有人问能支持properties吗?其时最第一版本是支持的,但咱们在前端页面把这个功能屏蔽了,由于咱们决定只支持yaml格式。
当Yml也不是彻底没有问题的,在实践过程当中,偶尔也出现有人把缩进搞错的状况。
使用Yml在线编辑器,能够很是方便编辑,好比:复制粘贴内容,就像在修改配置文件同样,尤为是批量修改时更为方便。不像其它经过key value方便管理的配置中心,每次修改都须要先找到相应的key才能进行一个个修改,很是费时费力;
Yml的JSON预览功能。当用户编辑内容时,会实时检查格式是否符合yaml格式时,若是格式是正确的,右则会正确显示其对应的json内容,若是格式不正确则,右则会提示相应的错误信息,能及时发现错误。
不停机实时刷新配置是配置中心的核心需求之一。好比在生产中运行的应用,忽然因需求或性能等缘由,须要调整配置,若是咱们还须要通过修改代码,从新打包,测试并部署等一系列的操做步骤的话,那效率可想可知,所以带来的损失也可能会很是之大。ConfigKeeper使用Spring Cloud提供的RefreshEndpoint刷新配置,在最初的版本中,咱们是经过curl或Postman等工具实现此功能,但这样操做效率比较差,为此在最新版本中增长了以下功能:
在此页面,咱们实现以下功能:
由于随行付从Spring boot 1.2.2版本就开始使用Spring boot,到如今已经实现全部应用boot化,因此咱们在设计配置中心时,其客户端必需要无缝兼容Spring boot、Spring cloud应用,因此咱们就参考Spring cloud config的实现。
为何ConfigKeeper能实现无缝兼容Spring boot、Spring cloud应用?其缘由很是简单,由于核心实现仍是由Spring cloud提供的,咱们只是在对Spring cloud进行扩展,而不是在其基础上从新造轮子。
要想学习客户端的源码的话,可能以/META-INF/spring.factories文件为入口,此文件中有以下配置:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\ com.suixingpay.config.client.SxfConfigServiceBootstrapConfiguration
而SxfConfigServiceBootstrapConfiguration存在以下代码:
@Bean @ConditionalOnMissingBean(SxfConfigServicePropertySourceLocator.class) @ConditionalOnProperty(value = "suixingpay.config.enabled", matchIfMissing = true) public SxfConfigServicePropertySourceLocator sxfConfigServicePropertySource(ApplicationContext context) { SxfConfigClientProperties configClientProperties = sxfConfigClientProperties(context); ConfigDAO configDAO = sxfConfigDAO(configClientProperties); return new SxfConfigServicePropertySourceLocator(configDAO, configClientProperties); }
而SxfConfigServicePropertySourceLocator其实就是PropertySourceLocator的实现类,其具体实现请你们查看源码文件。
在咱们实践后发现,使用配置中心,还能够很好地对配置进行治理,好比统一使用YAML格式配置,使用配置内容更加清晰;避免了使用jar来共享配置带来的一系列问题等等。但Spring boot、Spring cloud应用可加载的配置源很是之多,还须要注意一些问题。
从上面内容可见,Spring boot是支持很是多种方式加载配置的,并且支持重复配置以及支持覆盖,即相同key的配置,先加载的内容会被后加载的覆盖,为了方便后期维护,尽可能遵照如下原则:
相信不少人都会有这样的误区:全部的配置都是能够经过配置中心来实时刷新,否则配置中心的就没有多大意义了。为了解答这个问题,我先来看RefreshEndpoint都作了哪些事情:
public synchronized Set<String> refresh() { Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources()); // 加载最新配置到Environment addConfigFilesToEnvironment(); Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet(); // 发送EnvironmentChangeEvent this.context.publishEvent(new EnvironmentChangeEvent(context, keys)); // 清空RefreshScope缓存 this.scope.refreshAll(); return keys; }
经过上面的源码,咱们能够看出其RefreshEndpoint主要作了三件事情:
因此咱们要想获取最新配置配置,能够经过如下途径:
直接经过Environment获取,好比:
String applicationName = environment.getProperty("spring.application.name");
处理EnvironmentChangeEvent,好比对于线程池大小的调整,咱们能够监听EnvironmentChangeEvent,当接收到EnvironmentChangeEvent时,关闭原来的线程池,前从新实例化新的线程池;
Spring boot官方建议咱们尽可能咱们使用@ConfigurationProperties管理配置,那么它是否能自动刷新配置呢?其实它是能够的,由于在ConfigurationPropertiesRebinder中会监听EnvironmentChangeEvent,详细内容请查看org.springframework.cloud.context.properties. ConfigurationPropertiesRebinder。在实例化bean时增长@RefreshScope, 好比:
@Autowired private DefaultUserProperties userProperties; @RefreshScope // 支持动态刷新 @Bean(name="defaultUser") public UserDO defaultUser() { UserDO userDO=new UserDO(); userDO.setId(userProperties.getId()); userDO.setName(userProperties.getName()); return userDO; }
Spring cloud 为了实现运行时动态刷新,增长了RefreshScope(org.springframework.cloud.context.scope.refresh.RefreshScope类),会将加了@RefreshScope的bean放入RefreshScope中,当刷新RefreshScope时,会清空缓存,当下次使用这些bean时会从新实例些这些bean。
经过RefreshEndpoint 刷新的话,就须要开启Spring boot Endpoint相关功能,而Spring boot Endpoint若是不作特殊处理的话,很容易被探测到,引起一些安全问题。好比:
server: port: 8080 management: security: enabled: false
那么很容易去调用Spring boot Endpoint。生产环境的应用,安全问题不可忽视,因此建议作以下处理:
调整后的配置实例以下:
server: port: 8080 management: security: enabled: true context-path: /_ops port: 9098 security: basic: enabled: true path: ${management.context-path}/**, /swagger-ui.html, /v2/api-docs, /druid/** user: name: ma password: xxxxxx
Spring 生态功能很是丰富,为咱们解决了很是多棘手问题,但不少东西要进行本地化开发后才能更好的使用。配置中心使用了很多开源技术,给咱们带来了很多便利,但愿经过此开源项目回馈社区,为开源社区贡献绵薄之力。
https://github.com/sxfad/
https://gitee.com/sxfad/