配置中心

最后更新:2020-05-01

1. 为什么需要配置中心

我们先来看看在没有配置中心的传统项目中,我们是怎么处理各类配置参数问题的:

  1. 一般是静态化配置。大多数在项目中单独写一个配置文件,例如 “config.conf”,然后将各类 参数配置、应用配置、环境配置、安全配置、业务配置 都写到这个文件里。当项目代码逻辑中需要使用配置的时候,就从这个配置文件中读取。这种做法虽然简单,但如果参数需要修改,就非常的不灵活,甚至需要重启运行中的项目才能生效。
  2. 配置文件无法区分环境。由于配置文件是放在项目中的,但是我们项目可能会有多个环境,例如:测试环境、预发布环境、生产环境。每一个环境所使用的配置参数理论上都是不同的,所以我们在配置文件中根据不同环境配置不同的参数,这些都是手动维护,在项目发布的时候,极其容易因开发人员的失误导致出错。
  3. 配置文件过于分散。如果一个项目中存在多个逻辑模块独立部署,每个模块所使用的配置内容又不相同,传统的做法是会在每一个模块中都放一个配置文件,甚至不同模块的配置文件格式还不一样。那么长期的结果就是配置文件过于分散混乱,难以管理。
  4. 配置修改无法追溯。因为采用的静态配置文件方式,所以当配置进行修改之后,不容易形成记录,更无法追溯是谁修改的、修改时间是什么、修改前是什么内容。既然无法追溯,那么当配置出错时,更没办法回滚配置了。

沈大写过一篇说的比较清楚

https://mp.weixin.qq.com/s/k1IVjya7qtIf8jwWqTumjA

配置管理经历3个阶段

初期:“配置私藏”架构

“配置私藏”是配置的最初级阶段,上游调用下游,每个上游都有一个专属的私有配置文件,记录被调用下游的每个节点配置信息。

中期:“全局配置”架构

“全局配置”架构:对于通用的服务,建立全局配置文件,消除配置私藏:

(1)运维层面制定规范,新建全局配置文件,例如/opt/global.conf;

(2)对于服务方,如果是通用的服务,集群信息配置在global.conf里;

(3)对于调用方,调用方禁止配置私藏,必须从global.conf里读取通用下游配置;

(4)引入文件监控组件监控文件的变化,起一个timer,定期监控文件的ModifyTime或者md5就能轻松实现,当文件变化后,实施回调。

终版:“配置中心”架构

(1)调用方不需要再重启;

(2)服务方从配置中心中很清楚的知道上游依赖关系,从而实施按照调用方限流;

(3)很容易从配置中心得到全局架构依赖关系;

随着业务的发展、微服务架构的升级,服务的数量、程序的配置日益增多(各种微服务、各种服务器地址、各种参数),传统的配置文件方式和数据库的方式已无法满足开发人员对配置管理的要求:

  • 安全性:配置跟随源代码保存在代码库中,容易造成配置泄漏;
  • 时效性:修改配置,需要重启服务才能生效;
  • 局限性:无法支持动态调整:例如日志开关、功能开关;

因此,我们需要配置中心来统一管理配置。把业务开发者从复杂以及繁琐的配置中解脱出来,只需专注于业务代码本身,从而能够显著提升开发以及运维效率。同时将配置和发布包解藕也进一步提升发布的成功率,并为运维的细力度管控、应急处理等提供强有力的支持。

配置中心究竟要解决什么痛点?

上游痛:扩容的是下游,改配置重启的是上游;

下游痛:不知道谁依赖于自己;

总之,难以实施服务治理。

2. 配置中心

配置中心的思路就是把项目中各种配置、各种参数、各种开关,全部都放到一个集中的地方进行统一管理,并提供一套标准的接口。当各个服务需要获取配置的时候,就来配置中心的接口拉取。当配置中心中的各种参数有更新的时候,也能通知到各个服务实时的过来同步最新的信息,使之动态更新。

那么,按照上述思路,我们理想中的配置中心应该具备如下特点:

  • 配置集中管理、统一标准
  • 配置与应用分离
  • 实时更新
  • 高可用

对于配置中心的组成结构,我们可以做一层抽象

可以看到,对于一个典型的配置中心而言,存在两个组成部分,即配置服务器配置仓库

配置服务器的核心作用就是对接来自各个微服务的配置信息请求,这些微服务会通过配置服务器提供的统一接口获取存储在配置中心中的所需配置信息。因此,配置服务器也是作为独立的微服务而存在。对于配置服务器而言,一方面需要确保对配置中心中所存储的各种配置信息进行统一维护;另一方面,也需要提供一种通知机制,确保配置信息变化之后能够告知各个微服务,以便各个微服务及时更新本地服务中的配置数据(基于轮询或监听机制的实现方式)。

配置服务器可以独立完成配置信息的存储和维护工作,但也可以把这部分工作剥离出来放到单独的一个媒介中,这个媒介就是配置仓库。请注意,配置仓库并不是必备的,我们完全可以依托配置服务器自身的文件系统来实现配置信息的存储。但构建独立配置仓库的主要优势在于能够把配置存储过程进行抽象,从而支持 SVN、Gitlab 等具备版本控制功能的多种第三方工具,以及自建一个具有持久化或内存存储功能的存储媒介。

构建一个完整的配置中心并不简单,需要具备很多功能特性。首当其冲的就是隔离性,这里的隔离性指的是不同环境下配置信息之间应该是不会相互混淆的,例如测试环境的配置显然不应该作用于生产环境。然后,我们也需要有一种高效的机制来确保配置中心中的配置信息一旦有任何变化,能够及时通知到各个微服务,这也是配置一致性的一种表现。

2.1. 存储系统

其实配置文件不外乎就是一个key-value的配置项集合。当然,如果要支持多个环境,还有分组,顶多也就是加多几个字段,总体来说是一个非常简单的数据模型。另外,配置项更新并不会很频繁,配置生效时间其实要求也不会太高,数据量也不会很大。事务性方面要求会比较高的,丢失,不一致对于配置项来说是不可接受的。

所以,用普通的DB如MySQL就足够的。很多开源的配置中心采用zookeeper,其实是想利用zk的订阅功能,实现配置更新通知客户端。

2.2. 配置下发应用

以前的配置是随着应用一起发布的,应用启动时候加载本地配置文件的。现在配置数据统一发布到配置中心,所以需要一种机制讲数据下发到应用。

有两种方式:

  1. 推:这个实时性最高,但是需要应用与配置中心保持长连接。复杂性会高一些,特别是负载均衡这块。
  2. 拉:实时性相对差一些,如果不做增量更新的话对配置中心也会造成不必要的压力。不过实现会简单很多。

2.3. 变更通知

如果是推送的方式,那么server可以把每次变更都即使发送给订阅的客户端。 如果是拉取的方式,则可以通过比较client和server的数据的MD5值来实现有效变更通知。server会下发数据和md5给client,client请求的时候会带上md5,如果md5变化,则server会重新下发数据,否则返回“无变更”的状态码给client。

2.4. 灰度发布

有时候我们需要先把配置项灰度到某台机器看看效果如何,再决定是要全量还是回滚。

有两种解决方案:

  1. 配置项增加一个host属性,表示这个配置项只“发布”给某些IP。
  2. 定义一个优先级,客户端优先加载本地配置文件,这样如果某些机器上的应用需要特殊配置,那么可以采用老的方式上去修改其本地配置文件。

2.5. 高可用

除了变更通知以外,在配置中心实现中另外一个比较关键的点在于如何保证它的可用性。因为对于配置中心来说,可用性的重要程度要远远大于性能。

我们一般会在服务器启动时从配置中心中获取配置,如果配置获取的性能不高,那么外在的表现也只是应用启动时间慢了,对于业务的影响不大。但是,如果获取不到配置,很可能会导致启动失败。比如,我们把数据库的地址存储在了配置中心里,如果配置中心宕机导致我们无法获取数据库的地址,那么自然应用程序就会启动失败。因此,我们的诉求是让配置中心“旁路化”。也就是说,即使配置中心宕机,或者配置中心依赖的存储宕机,我们仍然能够保证应用程序是可以启动的。

我们一般会在配置中心的客户端上,增加两级缓存:第一级缓存是内存的缓存;另外一级缓存是文件的缓存。配置中心客户端在获取到配置信息后,会同时把配置信息同步地写入到内存缓存,并且异步地写入到文件缓存中。内存缓存的作用是降低客户端和配置中心的交互频率,提升配置获取的性能;而文件的缓存的作用就是灾备,当应用程序重启时,一旦配置中心发生故障,那么应用程序就会优先使用文件中的配置,这样虽然无法得到配置的变更消息(因为配置中心已经宕机了),但是应用程序还是可以启动起来的,算是一种降级的方案。

3. 实践经验

本节来自于个推基于Consul的配置管理经验 https://mp.weixin.qq.com/s/Xr49dQfafEiJX2I0CPaawQ

如果用其他配置中心也级别差不多

在实践中,不同产品线的配置会放置在Consul的不同路径下,实现不同产品线配置之间的隔离。

按照配置的用途,可将同一产品线下的配置分为三类:

  1. API网关相关配置;
  2. 服务注册与发现相关配置;
  3. 应用相关配置。

其中,每类配置会对应Consul上的不同目录。

按照配置的变化特性,可将配置分为两类:

  1. 环境相关的全局配置,如MySQL等外部依赖相关的配置和其他与环境相关的配置,这类配置在开发测试生产环境中存在差异,需要为不同环境配置不同的值。
  2. 应用本身的配置,一般为不经常性发生变化、可动态调整、开关的配置。这类配置比较稳定,在初始化后,只有在需要时才会改动,通常会设置默认值。这两类配置在Consul上会放在不同的子目录下。这样QA、运维只需要关注环境差异部分即可。

基于以上对配置的分类,最终Consul上的Key的格式如下:

/ProductLine_Prefix/Usage_Prefix/Environmental_Correlation_Prefix/Config_Item_Path

其中:

  • ProductLine_Prefix:用来隔离不同产品线的配置;
  • Usage_Prefix:用来区分配置的用途;
  • EnvironmentalCorrelationPrefix:用来分隔与环境相关的配置;
  • ConfigItemPath:具体的配置项。

配置在Consul上的组织形式有以下两种:

  1. 以配置文件的形式组织,Consul上的一个K/V,对应一个配置文件,如nginx的配置文件。
  2. 以配置项的形式组织,将配置文件模板化,拆成一个个的配置项,每个配置项对应Consul上的一个K/V,多个配置项对应一个配置文件。大部分配置文件本身都是以K/V的形式组织的,均适合模板化,模板化后即可以按照配置项的特性,在Consul上分成不同的类别进行管理。

4. 开源实现

  • Apollo
  • Qconf
  • Disconf
  • Spring Cloud Config
  • Nacos

5. 参考资料

https://mp.weixin.qq.com/s/k1IVjya7qtIf8jwWqTumjA

https://zhuanlan.zhihu.com/p/66097586

http://arganzheng.life/configuration-center-implementation.html

Edgar

Edgar
一个略懂Java的小菜比