分布式配置中心
什么是配置
配置(Configuration) 这个概念每个技术人都不陌生,可以说一个不提供几个配置参数的系统都不好意思上线跟别的系统打招呼。
究其本质是我们人类无法掌控和预知一切,映射到软件领域上,我们总是需要对系统的某些功能特性预留出一些控制的线头,以便我们在未来需要的时候,可以人为的拨弄这些线头从而控制系统的行为特征,我把它叫做 “系统运行时(runtime)飞行姿态的动态调整“。
静态配置
我们的系统存在一些外部依赖,这些依赖与业务无关,但是在开发阶段具有不确定性,我们通常会将这些配置在属性文件中,如数据库url,账户名,密码,等,这些配置项基本不会轻易改变,而且配置也简单,基本都是字符串,所以称他们为静态配置
所谓静态配置,就是在程序启动前一次性配好,启动时一次性生效,在程序运行期一般不会变化的配置。
具体包括:
环境相关配置
有些配置是和环境相关的,每个环境的配置不一样,例如数据库、中间件和其它服务的连接字符串配置。这些配置一次性配好,运行期一般不变。
安全配置
有些配置和安全相关,例如用户名,密码,访问令牌,许可证书等,这些也是一次性配好,运行期一般不变。因为涉及安全,相关信息一般需要加密存储,对配置访问需要权限控制。
动态配置
我们在业务开发中,会提前预料到业务的不确定性,而产生的复杂的业务配置项,类型简单的可以使数字,字符,复杂点的可以是数组,map,以及其混合的组织形式,由于业务需求,会需要常常发生改变,这类配置项,我们称为动态配置。
所谓动态配置,就是在程序的运行期可以根据需要动态调整的配置。动态配置让应用行为和功能的调整变得更加灵活,是持续交付和 DevOps 的最佳实践。
应用配置
和应用相关的配置,例如服务请求超时,线程池和队列的大小,缓存过期时间,数据库连接池的容量,日志输出级别,限流熔断阀值,服务安全黑白名单等。一般开发或者运维会根据应用的实际运行情况调整这些配置。
业务配置
和业务相关的一些配置,例如促销规则,贷款额度,利率等业务参数,A/B 测试参数等。一般产品运营或开发人员会根据实际的业务需求,动态调整这些参数。
功能开关
在英文中也称 Feature Flag/Toggle/Switch,简单的只有真假两个值,复杂的可以是多值参数。功能开关是 DevOps 的一种最佳实践,在运维中有很多应用场景,比如蓝绿部署,灰度开关,降级开关,主备切换开关,数据库迁移开关等。功能开关在国外互联网公司用得比较多,国内还没有普及开,所以我在下一节会给出一些功能开关的高级应用场景。
为什么要有配置中心
在单机时代,我们都是用配置文件来存储配置项。一个配置文件一般是一组配置项的集合或者叫配置集,一个系统根据逻辑模块划分,可以有1到多个配置文件。
在集中式开放时代,配置文件基本够用了,因为那是配置的管理通常不会成为一个很大的问题。如果要修改一个配置,登录到这台机器上,登录到这台生产机器上,vi配置这个配置文件,然后reload一下并不是很大的负担。
在分布式系统中,一次构建、发布、上线是非常非常重的一个过程,它不像单机时代那样重启一台机器、一个进程就可以了。
时效性
在分布式系统中,它涉及到将软件包(例如war)分发到可能超过几千台机器,然后将几千台机器上的应用进程一一重启这么一个过程,超过2000台机器的一个应用一次完整的发布过程需要多长时间,相信大家都一定会清楚,所以从时效性上来说,配置中心是势在必行。
统一管理
分发到几千台机器上,谁能保证几千次配置都会一致,不会有哪个人手误导致文件更改有错误,所以有一个应用统一管理管理配置,之后其他应用需要更改本地配置的时候,来向配置中心拉取,之后替换配置,之后重新发布就好了,这样就能保证所有机器上的配置的都一致。
分布式三项原则
在分布式计算技术的设计和实现中,CAP理论是一个重要的指导原则,其基本内容如下:
“C”是指一致性,即当一个Process(过程)修改了某个数据后,其他Process读取这是数据是,得到的是更新后的数据,但并不是所有系统都 可以做到这一点。例如,在一些并非严格要求一致性的系统中,后来的Process得到的数据可能还是修改之前的数据,或者需要等待一定时间后才能得到修改 之后的数据,这被成为“弱一致性”,最经典的应用就是DNS系统。当用户修改了DNS配置后,往往不会马上在全网更新,必定会有一个延迟,这个延迟被称为 “不一致窗口”,它的长度取决于系统的负载、冗余的个数等因素。但对于某些系统而言,一旦写入,后面读取的一定是修改后的数据,如银行账户信息,这被称为 “强一致性”。
“A”是指可用性。即系统总是能够为用户提供连续的服务能力。当用户发出请求是,系统能给出响应(成功或者失败),而且是立即给出响应,而不是等待其他事情完成才响应。如果需要等待某件事情完成才响应,那么“可用性”就不存在了。
“P”是指容错性。任何一个分布式计算系统都是由多个节点组成的。在正常情况下,节点与节点之间的通信是正常的。但是在某些情况下,节点之间的通信会 断开,这种断开成为“Partition”。在分布式计算的实现中,Partition是很常见的,因为节点不可能永远不出故障,尤其是对于跨物理地区的 海量存储系统而言,而容错性则可以保证如果只是系统中的部分节点不可用,那么相关的操作仍旧能够正常完成。
演进中的配置中心
经过上面的内容,大家知道肯定要有一个配置中心来统一管理,通过配置中心系统对每一条配置(每一个配置有唯一的配置ID)进行增删改查。区分不同环境的配置,每个环境同一配置ID对应不同数据库记录。配置最终以key-value储存在mysql数据库中。
模式1
可以有一个配置中心,通过配置中心对每一条配置进行增删改查,配置最终以key-value储存在mysql数据库,配置对外服务,多机器部署,满足性能需要。应用直接去调用配置中心提供的接口查询配置,这样可能对数据库压力太大,这样的话可以在配置中心添加缓存,每次查询缓存,这样的话每次查询的时候,就不会调用数据库,每次可用直接查询缓存,每次更改配置的时候,顺便更改缓存,这样能保证可用性和一致性。
很多时候,这样可以基本上满足我们对配置系统的基本需求,对配置的增删改查,能容忍一段时间的数据不一致性。
这种设计,由于所有的配置都存放在集中式缓存中,这样集中式的缓存也会有他的性能瓶颈。而且,每次配置的访问都需要发起rpc请求(网络请求),因此考虑在客户端引入本地缓存(localCache,例如Ehcache)。
模式2
能在服务端使用缓存,为什么不能再客户端使用缓存呢。
使用客户端缓存,那么缓存存在哪呢?
在客户端使用缓存可以分成三种,分布式缓存,内存和硬盘。
但是这三种各有利弊。
分布式缓存 那么网络资源的耗费没有得到节约,但是可以保证应用读取的配置一致性的保证。
内存 不用耗费网络资源,很快,但是重启会丢失配置
硬盘 不用耗费网络资源,不会丢失配置,相对来说会慢一些
考虑到,减少网络请求的因素,在客户端引入localcache,虽然内存和硬盘可以节省网络资源,但是内存和硬盘都存在各自的缺点,那么我们可以采用内存+硬盘的格式,把资源保存再硬盘里,每次重启时在从硬盘中读取之后加载到内存中。这样的话来解决系统的高可用,高性能、可伸缩性。
一致性的话的我们可以采用缩短更新缓存的问题来解决。也可以通过配置中心通知应用配置更新,应用从配置中心拉取最新的配置、更新本地配置并通知到应用。
开源配置中心对别
功能特性
我们先从功能层面来对别
功能点 | 优先级 | spring-cloud-config | apollo(携程2016) | disconf(百度2015) | 备注 |
---|---|---|---|---|---|
静态配置管理 | 高 | 基于file | 支持 | 支持 | |
动态配置管理 | 高 | 支持 | 支持 | 支持 | |
统一管理 | 高 | 无,需要github | 支持 | 支持 | |
多环境 | 中 | 无,需要github | 支持 | 支持 | |
本地配置缓存 | 高 | 无 | 支持 | 支持 | |
配置锁 | 中 | 支持 | 不支持 | 不支持 | 不允许动态及远程更新 |
配置校验 | 中 | 无 | 无 | 无 | 如:ip地址校验,配置 |
配置生效时间 | 重启生效,或手动refresh生效 | 实时 | 实时 | 需要结合热加载管理, springcloudconfig需要 git webhook+rabbitmq 实时生效 | |
配置更新推送 | 高 | 需要手工触发 | 支持 | 支持 | |
配置定时拉取 | 高 | 无 | 支持 | 配置更新目前依赖事件驱动, client重启或者server端推送操作 | |
用户权限管理 | 中 | 无,需要github | 支持 | 支持 | 现阶段可以人工处理 |
授权、审核、审计 | 中 | 无,需要github | 支持 | 无 | 现阶段可以人工处理 |
配置版本管理 | 高 | Git做版本管理 | 界面上直接提供发布历史和回滚按钮 | 操作记录有落数据库,但无查询接口 | |
配置合规检测 | 高 | 不支持 | 支持(但还需完善) | ||
实例配置监控 | 高 | 需要结合springadmin | 支持 | 支持,可以查看每个配置在哪些机器上加载 | |
灰度发布 | 中 | 不支持 | 支持 | 不支持部分更新 | 现阶段可以人工处理 |
告警通知 | 中 | 不支持 | 支持,邮件方式告警 | 支持,邮件方式告警 | |
依赖关系 | 高 | 不支持 | 不支持 | 不支持 | 配置与系统版本的依赖系统运行时的依赖关系 |
技术路线兼容性
引入配置中心,需要考虑和现有项目的兼容性,以及是否引入额外的第三方组件。我们的java项目以SpringBoot为主,需要重点关注springboot支持性。
功能点 | 优先级 | spring-cloud-config | apollo(携程2016) | disconf(百度2015) | 备注 |
SpringBoot支持 | 高 | 原生支持 | 支持 | 与spring boot无相关 | |
SpringCloud支持 | 高 | 原生支持 | 支持 | 与spring cloud无相关 | |
客户端支持 | 低 | Java | Java、.Net | java | |
业务系统侵入性 | 高 | 侵入性弱 | 侵入性弱 | 侵入性弱,支持注解及xml方式 | |
依赖组件 | 高 | Eureka | Eureka | zookeeper |
可用性与易用性
引入配置中心后,所有的应用都需要依赖配置中心,因此可用性需要重点关注,另外管理的易用性也需要关注。
功能点 | 优先级 | spring-cloud-config | apollo(携程2016) | disconf(百度2015) | 备注 |
---|---|---|---|---|---|
单点故障(SPOF) | 高 | 支持HA部署 | 支持HA部署 | 支持HA部署,高可用由zookeeper保证 | |
多数据中心部署 | 高 | 支持 | 支持 | 支持 | |
配置获取性能 | 高 | unkown | unkown(官方说比spring快) | ||
配置界面 | 中 | 无,需要通过git操作 | 统一界面(ng编写) | 统一界面 |
开源配置中心对比矩阵
Spring Cloud Config
Spring Cloud Config微服务端和客户端提供了分布式系统的外部化配置支持。配置服务器为各应用的所有环境提供了一个中心化的外部配置。它实现了对服务端和客户端对Spring Environment和PropertySource抽象的映射,所以它除了适用于Spring构建的应用程序,也可以在任何其他语言运行的应用程序中使用。作为一个应用可以通过部署管道来进行测试或者投入生产,我们可以分别为这些环境创建配置,并且在需要迁移环境的时候获取对应环境的配置来运行。
好处
当我们系统业务扩展到一定程度的时候;免不了会增加很多的配置文件和信息,例如证书文件、接口对接的参数信息、数据库连接信息等;传统的单体式架构系统,SSH、SSM还是Struts等,只能是一个文件一个文件的增加堆积到项目系统中。每次更改配置信息的时候,都要重启服务器,影响线上业务浪费时间等。当配置文件数量达到一定程度的时候,整个项目就会看起来非常臃肿冗余,更甚者可能会拿错配置信息导致程序崩溃等。那么,这时候分布式系统采用的配置中心的优势就突出出来了。由业务拆分的多个模块系统的各配置文件,全部配置在配置中心统一管理;与程序分离,做到动态配置获取配置信息。无需重启服务器即可动态刷新加载配置信息。
组成部分
Spring Cloud Config 分为服务端和客户端两部分
服务端
服务端也成为分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器,并为客户端提供获取配置信息、加密解密信息灯访问接口
客户端
客户端则是通过指定的配置中心来管理应用资源以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息,配置服务器默认使用 git 来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过 git 客户端工具来方便的管理和访问配置内容
能做什么
- 集中管理配置文件
- 不同环境不同配置,动态化的配置更新,分环境部署,比如dev/prod/test/beta/release
- 运行期间动态调整配置,不再需要在每个服务上编写配置文件,服务会向配置中心统一拉取自己的配置
- 当配置发生变动时,服务无需重启,可以动态的应用新配置
- 将配置信息以 REST 接口的形式暴露给微服务
与GitHub整合配置
Spring Cloud Config 默认使用 Git 来存储配置文件(也有其他方式,比如SVN、本地文件,但最推荐的还是 Git),而且使用的是 http/https 访问的形式