如何写个分布式配置中心

前言

一位读者朋友跟我反馈,能不能写一篇比较全的配置中心的文章。自己最近在面试过程中有被面试官问:如何设计一个配置中心? 这个话题,由于自己在工作中也没实际使用过配置中心,所以对于如何去设计是完全没有概念的。

今天就给大家写一篇去配置中心需要考虑的点,我也不是什么配置中心开源项目的参与者,所以写出来的仅供大家参考。

有必要重复造轮子吗?

当面试官问你:如果让你写一个配置中心,说说你的设计思路? 首先我们要有自己的想法,虽然是在面试过程中的问题。我们也可以反问,市面上目前有几款很优秀的开源的配置中心,我们可以直接拿来用,有必要去重新造轮子吗?

如果面试官说只是考察一下你对这块的设计和理解程度,然后你就可以接着讲解你的思路了。如果面试官说我们很多框架都是自研的,任性,就是有这个需求,那你还是得接着说,躲不掉,哈哈。

如果要重新造,我们也可以基于开源的进行改造,下面我说下如果要设计一个配置中心,它的整体思路是怎样的,需要用到哪些技术点,然后开始你的表演。

配置存储选型

首先我们来看存储的选型,配置中心需要存储所有的配置内容,肯定需要进行存储。目前主流开源的配置中心都采用 Mysql 进行配置的存储,当然你也可以用其他的,比如 MongoDB 也非常适合。

用不同的数据库在设计表结构的时候会有所不同,比如 Mysql 可能要 10 个表,MongoDB 简化后可能 5 个表就够了(Mysql 多表关联,MongoDB 内嵌文档)。

Mysql 多表关联

面试官:如果让你写个分布式配置中心,就问你慌不慌

MongoDB 内嵌文档

面试官:如果让你写个分布式配置中心,就问你慌不慌

这些表除了基本的配置内容存储,还有就是一些辅助的表,比如用户信息,权限信息等。

除了底层结构的设计,我们还需要考虑存储的可用性。Mysql 可以做主从,分库分表等,MongoDB 天生就是分布式的数据库,也不存在单点问题,在可用性这块都是 OK 的。

另外在设计层面,对于配置信息可以加上本地缓存,当数据库或者服务不可用时也能短暂提供服务能力,一般都是在 client 层面做。Apollo 和 Nacos 都会在本地缓存配置信息。

配置隔离

配置隔离在配置中心也是非常重要的一个点,不同的环境不同的配置信息,这个是最基础的。在没使用配置中心之前通常都是在项目中为每个环境维护一个配置文件,然后通过命令进行切换需要使用的文件。

面试官:如果让你写个分布式配置中心,就问你慌不慌

除了环境的隔离,还有一种就是访问层面的隔离,比如命名空间,不同的空间相互是隔离的,不能相互访问。

底层隔离的方式也有很多种,第一种是在存储的时候增加一个字段进行环境的区分,数据统一存储在一起,但是可以区分,这种方式好处在于一套配置中心可以提供给所有环境进行使用。

面试官:如果让你写个分布式配置中心,就问你慌不慌

第二种是在部署层面直接就隔离了,也就是测试环境部署一套独立的配置中心,线上也部署一套独立的配置中心,也就不需要在存储的时候通过字段隔离了。

面试官:如果让你写个分布式配置中心,就问你慌不慌

第三种也是部署的时候进行隔离,不同的点在于 Web 后台管理只部署一套,配置信息对应的服务可以按环境部署多套,每套都有自己独立的数据库,Apollo 就是采用这种方式。

面试官:如果让你写个分布式配置中心,就问你慌不慌

配置推送刷新

配置在修改后能够实时的推送到应用程序中进行更新,这个是最重要的一个功能,用户体验也是非常好的。在没用配置中心之前,有用 Mysql 进行配置存储的,为了提高性能,减小数据库的压力,配置信息读取后会放入缓存中,后台会启动一个定时线程去更新,比如 1 分钟一次。

这样带来的问题就是配置改完后需要等待一定的时间客户端才能更新好,一般场景都没啥问题,对于一些特殊的场景还是需要改完立马生效,才能尽可能避免某些业务问题带来的损失。

对于配置修改及时更新的实现方式目前主要分为两种:推和拉。

拉模式前面讲过了,有时间间隔问题,就算设置的很快,比如 1 秒一次,频率太高会导致服务端压力过大。

推模式是比较好的方式,当服务端有变动的时候将变更的信息推送给客户端,即及时又能减轻定时拉取的频率。

推送可以采用 Spring DeferredResult 将请求挂起的模式实现,详情可以参考我的这篇文章:

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

更好的方式是推拉结合,目前主流的配置中心都是采用这种方式。推保证及时性,拉用于兜底,保证最终配置一致性,推拉结合的模式可以将拉取的时间放长,降低服务端压力。

集成 Spring

Spring 是 Java 语言开发必不可少的好朋友,使用 Spring 可以极高的提高我们的开发效率,各种框架都能非常方便的集成。

在 Spring 中最常见的两种获取配置值的方式是@Value 和@ConfigurationProperties,要想使用上面的方式能够获取到配置中心里的内容,需要在项目启动的时候从配置中心加载对应的配置内容,然后集成到 Spring 中。

Spring 中提供了 ConfigurableEnvironment,ConfigurableEnvironment 中又包含多个 PropertySource。PropertySource 就是 Key,Value 的配置。所以需要在应用启动的时候,获取配置信息组装成 PropertySource 交给 Spring 管理。

权限审计

无论是所有环境用一套配置中心还是每个环境都有单独的部署,权限控制还是要的,因为不同的小组负责不同的业务,肯定不能随便去改动其他组的配置。

另一个场景就是配置能被谁改,这个一般都是负责人进行修改,团队人员可以查看配置信息,这个也是很常见需要进行控制的场景。

面试官:如果让你写个分布式配置中心,就问你慌不慌

单纯从配置的功能来讲,很多人都会说为什么我要用配置中心,自己搞张表存储一下不也行么,我认为配置的存储是最基本的功能,更多让我们使用配置中的原因在于可以节省我们自己去做的成本。同时配置中心具有很全的治理方面的能力,比如权限,灰度实用的功能等。

指标监控

作为一款中间件,而且是被很多系统使用,它的一些性能指标也是需要监控起来的。常见的做法有下面几种方式。

一种是配置中心自己暴露出一些指标数据,可以让外部监控系统进行拉取,pull 方式。像 Nacos 中就暴露了 metrics 数据,可以用 prometheus 进行拉取并监控,非常方便。

面试官:如果让你写个分布式配置中心,就问你慌不慌

一种是配置中心自己埋点,对接一些监控系统,采用 push 的方式。比如 Apollo 中就集成了 Cat 的监控,可以将相关监控数据投递到 Cat 中进行展示并告警。

还有一种方式就是提供 Tracer 相关的 SPI,可以让使用方自行去接入不同的监控,灵活度更高。

无论用哪种方式,我们的最终目的是一致的,都是为了能够让 Bug 不发生,就算有问题也能监控到,不然就惨了,哈哈。

开放 API/多语言 SDK

目前几款比较活跃的配置中心都是 Java 开发的,也提供了对应的 Java SDK。如果你自己用其他语言开发一套配置中心,也是一样的需要有对应语言的 SDK。如果公司是多语言技术栈,那么可以为每种语言都开发一个 SDK 进行接入。

如果作为开源的项目,也不能规定别人使用什么语言,如果不想开发多语言 SDK 的话,可以提供一套开放 API 让使用者自己去封装 SDK 或者直接在项目中进行接入。

您可能还会对下面的文章感兴趣: