极客时间已完结课程限时免费阅读

33 | 配置管理:成千上万的配置项要如何管理?

33 | 配置管理:成千上万的配置项要如何管理?-极客时间

33 | 配置管理:成千上万的配置项要如何管理?

讲述:唐扬

时长11:26大小10.49M

你好,我是唐扬。
相信在实际工作中,提及性能优化你会想到代码优化,但是实际上有些性能优化可能只需要调整一些配置参数就可以搞定了。为什么这么说呢?我给你举几个例子:
你可以调整配置的超时时间让请求快速失败,防止系统的雪崩,提升系统的可用性;
你还可以调整 HTTP 客户端连接池的大小,来提升调用第三方 HTTP 服务的并行处理能力,从而提升系统的性能。
你可以认为配置是管理你系统的工具,在你的垂直电商系统中,一定会有非常多的配置项,比如数据库的地址、请求 HTTP 服务的域名、本地内存最大缓存数量等等。
那么,你要如何对这些配置项做管理呢?管理的过程中要注意哪些事情呢?

如何对配置进行管理

配置管理由来已久,在 Linux 系统中就提供了大量的配置项,你可以根据自身业务的实际情况,动态地对系统功能做调整。比如,你可以通过修改 dirty_writeback_centisecs 参数的数值,调整 Page Cache 中脏数据刷新到磁盘上的频率;你也可以通过修改 tcp_max_syn_backlog 参数的值,来调整未建立连接队列的长度。而你既可以通过修改配置文件并且重启服务器来让配置生效,也可以通过 sysctl 命令来动态地调整,让配置即时生效。
那么在开发应用的时候,都有哪些管理配置的方式呢?我觉得主要有两种:
一种是通过配置文件来管理;
另一种是使用配置中心来管理。
以电商系统为例,你和你的团队在刚开始开发垂直电商系统时,为了加快产品的研发速度,大概率不会注意配置管理的问题,会自然而然地把配置项和代码写在一起。但是随着配置项越来越多,为了更好地对配置项进行管理,同时避免修改配置项后还要重新对代码做编译,你选择把配置项拆分成独立的文件(文件可以是 properties 格式、xml 格式或 yaml 格式)。不过,这些文件还是会和工程一起打包部署,只是更改配置后不需要重新编译代码了。
随后,你很快发现了一个问题:虽然把配置拆分了出来,但是由于配置还是和代码打包在一起,如果要更改一个配置还是需要重新打包,这样无疑会增加打包的时间。于是,你考虑把配置写到单独的目录中,这样,修改配置就不需要再重新打包了(不过,由于配置并不能够实时生效,所以想让配置生效,还是需要重启服务)。
我们一般使用的基础组件,比如 Tomcat、Nginx,都是采用上面这种配置文件的方式来管理配置项的,而在 Linux 系统中,我提到的 tcp_max_syn_backlog 就可以配置在 /etc/sysctl.conf 中。
这里,我需要强调一点,我们通常会把配置文件存储的目录标准化为特定的目录。比如,都配置成 /data/confs 目录,然后把配置项使用 Git 等代码仓库管理起来。这样,在增加新的机器时,在机器初始化脚本中只需要创建这个目录,再从 Git 中拉取配置就可以了。这是一个标准化的过程,可以避免在启动应用时忘记部署配置文件。
再进一步说,如果你的服务是多机房部署的,那么不同机房的配置项中有可能是相同的,也有可能是不同的。这时候,你需要将相同的配置项放置在一个目录中给多个机房共用,再将不同的配置项放置在以机房名为名称的目录中。在读取配置的时候应该优先读取机房的配置,再读取公共配置,这样可以减少配置文件中的配置项的数量。
我给你列了一个典型目录配置,如果你的系统也使用文件来管理配置,可以参考一下。
/data/confs/common/commerce //电商业务的公共配置
/data/confs/commerce-zw //电商业务兆维机房配置
/data/confs/commerce-yz //电商业务亦庄机房配置
/data/confs/common/community //社区业务的公共配置
/data/confs/community-zw //社区业务兆维机房配置
/data/confs/community-yz //社区业务亦庄机房配置
那么,这是不是配置管理的最终形态呢?当然不是,你不要忘了把配置放在文件中的方式还存在的问题(我上面也提到过了),那就是,我们必须将服务重启后才能让配置生效。有没有一种方法可以在不重启应用的前提下也能让配置生效呢?这就需要配置中心帮助我们实现了。

配置中心是如何实现的

配置中心可以算是微服务架构中的一个标配组件了。业界也提供了多种开源方案供你选择,比较出名的有携程开源的 Apollo、百度开源的 Disconf、360 开源的 QConf、Spring Cloud 的组件 Spring Cloud Config 等等。
在我看来,Apollo 支持不同环境,不同集群的配置,有完善的管理功能,支持灰度发布、更改热发布等功能,在所有配置中心中功能比较齐全,推荐你使用。
那么,配置中心的组件在实现上有哪些关键的点呢?如果你想对配置中心组件有更强的把控力,想要自行研发一套符合自己业务场景的组件,又要如何入手呢?

配置信息如何存储

其实,配置中心和注册中心非常类似,其核心的功能就是配置项的存储和读取。所以,在设计配置中心的服务端时,我们需要选择合适的存储组件来存储大量的配置信息,这里可选择的组件有很多。
事实上,不同的开源配置中心也使用了不同的组件,比如 Disconf、Apollo 使用的是 MySQL;QConf 使用的是 ZooKeeper。我之前维护和使用的配置中心就会使用不同的存储组件,比如微博的配置中心使用 Redis 来存储信息,而美图用的是 Etcd。
无论使用哪一种存储组件,你所要做的就是规范配置项在其中的存储结构。比如,我之前使用的配置中心用 Etcd 作为存储组件,支持存储全局配置、机房配置和节点配置。其中,节点配置优先级高于机房配置,机房配置优先级高于全局配置。也就是说,我们会优先读取节点的配置,如果节点配置不存在,再读取机房配置,最后读取全局配置。它们的存储路径如下:
/confs/global/{env}/{project}/{service}/{version}/{module}/{key} //全局配置
/confs/regions/{env}/{project}/{service}/{version}/{region}/{module}/{key} //机房配置
/confs/nodes/{env}/{project}/{service}/{version}/{region}/{node}/{module}/{key} //节点配置

变更推送如何实现

配置信息存储之后,我们需要考虑如何将配置的变更推送给服务端,这样就可以实现配置的动态变更,也就是说不需要重启服务器就能让配置生效了。而我们一般会有两种思路来实现变更推送:一种是轮询查询的方式,一种是长连推送的方式。
轮询查询很简单,就是应用程序向配置中心客户端注册一个监听器,配置中心的客户端,定期地(比如 1 分钟)查询所需要的配置是否有变化,如果有变化则通知触发监听器,让应用程序得到变更通知。
这里有一个需要注意的点,如果有很多应用服务器都去轮询拉取配置,由于返回的配置项可能会很大,那么配置中心服务的带宽就会成为瓶颈。为了解决这个问题,我们会给配置中心的每一个配置项多存储一个根据配置项计算出来的 MD5 值。
配置项一旦变化,这个 MD5 值也会随之改变。配置中心客户端在获取到配置的同时,也会获取到配置的 MD5 值,并且存储起来。那么在轮询查询的时候,需要先确认存储的 MD5 值和配置中心的 MD5 是不是一致的。如果不一致,这就说明配置中心里存储的配置项有变化,然后才会从配置中心拉取最新的配置。
由于配置中心里存储的配置项变化的几率不大,所以使用这种方式后,每次轮询请求就只是返回一个 MD5 值,可以大大地减少配置中心服务器的带宽。
另一种长连的方式,它的逻辑是在配置中心服务端保存每个连接关注的配置项列表。这样当配置中心感知到配置变化后,就可以通过这个连接把变更的配置推送给客户端。这种方式需要保持长连,也需要保存连接和配置的对应关系,实现上要比轮询的方式复杂一些,但是相比轮询方式来说,能够更加实时地获取配置变更的消息。
而在我看来,配置服务中存储的配置变更频率不高,所以对于实时性要求不高,但是期望实现上能够足够简单,那么如果选择自研配置中心的话,可以考虑使用轮询的方式。

如何保证配置中心高可用

除了变更通知以外,在配置中心实现中另外一个比较关键的点在于如何保证它的可用性。因为对于配置中心来说,可用性的重要程度要远远大于性能。
我们一般会在服务器启动时从配置中心中获取配置,如果配置获取的性能不高,那么外在的表现也只是应用启动时间慢了,对于业务的影响不大。但是,如果获取不到配置,很可能会导致启动失败。
比如,我们把数据库的地址存储在了配置中心里,如果配置中心宕机导致我们无法获取数据库的地址,那么自然应用程序就会启动失败。因此,我们的诉求是让配置中心“旁路化”。也就是说,即使配置中心宕机,或者配置中心依赖的存储宕机,我们仍然能够保证应用程序是可以启动的。那么这是如何实现的呢?
我们一般会在配置中心的客户端上,增加两级缓存:第一级缓存是内存的缓存;另外一级缓存是文件的缓存。
配置中心客户端在获取到配置信息后,会同时把配置信息同步地写入到内存缓存,并且异步地写入到文件缓存中。内存缓存的作用是降低客户端和配置中心的交互频率,提升配置获取的性能;而文件的缓存的作用就是灾备,当应用程序重启时,一旦配置中心发生故障,那么应用程序就会优先使用文件中的配置,这样虽然无法得到配置的变更消息(因为配置中心已经宕机了),但是应用程序还是可以启动起来的,算是一种降级的方案。

课程小结

以上就是本节课的全部内容了。在这节课中,我带你了解了系统开发的过程中,我们是如何管理大量的配置项的,你需要了解的重点是:
配置存储是分级的,有公共配置,有个性的配置,一般个性配置会覆盖公共配置,这样可以减少存储配置项的数量;
配置中心可以提供配置变更通知的功能,可以实现配置的热更新;
配置中心关注的性能指标中,可用性的优先级是高于性能的,一般我们会要求配置中心的可用性达到 99.999%,甚至会是 99.9999%。
这里你需要注意的是,并不是所有的配置项都需要使用配置中心来存储,如果你的项目还是使用文件方式来管理配置,那么你只需要将类似超时时间等,需要动态调整的配置,迁移到配置中心就可以了。对于像是数据库地址,依赖第三方请求的地址,这些基本不会发生变化的配置项,可以依然使用文件的方式来管理,这样可以大大地减少配置迁移的成本。

一课一思

结合实际情况谈一谈,你的项目中配置管理的方式是怎样的呢?欢迎在留言区与我分享你的经验。
最后,感谢你的阅读,如果这篇文章让你有所收获,也欢迎你将它分享给更多的朋友。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 8

提建议

上一篇
32 | 压力测试:怎样设计全链路压力测试平台?
下一篇
34 | 降级熔断:如何屏蔽非核心系统故障的影响?
 写留言

精选留言(27)

  • 台风骆骆
    2019-12-09
    学习本篇文章收获: 1、配置中心的配置是需要分级的,如全局,机房,个性,可以减少存储,提高复用性。 2、当前配置中心主要有拉和推两种,拉就是定时去轮循获取,这里为了减少带宽,一开始是拿着本地的md5去看下是否配置有变更,如果没有变更,就不用拉取,如果有变更,再去拉取新的配置项。推就是需要保持长连接了,服务端还要维护客户端那边关注的配置项,一旦有配置项变更,就通知客户端。 3、配置中心是可用性高于性能的,配置中心客户端除了要维护内存配置项,还要维护一个文件的配置项,这个是异步同步的,一旦配置中心服务器挂了,可以降级读取本地文件的配置项内容。
    展开
    共 1 条评论
    22
  • 丁丁历险记
    2019-12-26
    md5 值判断更新,这个套路常用。

    作者回复: 是的,很常见

    14
  • 👽
    2019-12-19
    个人总结: 配置中心:就相当于云。将配置内容挂载到云上,所有服务器根据自己的情况取自己需要的配置。 配置分级:类似于面向对象编程中子类和父类的。父类已有的方法可以满足要求时,可以直接用。如果自己有额外业务,那就复写父类的方法。 配置变更通知:类似于设计模式的发布订阅模式。再有新配置内容发布的时候,要保证通知所有需要同步配置的客户端。 配置中心性能指标: 因为配置中心并发量与请求响应时间的要求并不高。因为一个客户端可能很久才需要更新一次配置,所以可用性才是最主要的指标。
    展开

    作者回复: 赞

    共 2 条评论
    11
  • longslee
    2019-12-17
    打卡。 zk 是 满足 CP,那么那几个基于 zk 的开源配置中心是怎么做到高可用的呢? 呃,我们的配置,似乎是反过来的哈哈,由一个配置中心定期生成一个静态文件,客户端直接引入这个路径下的文件...

    作者回复: 一般就是在客户端会有一个灾备文件,有的还会在服务端做多级缓存。

    共 3 条评论
    6
  • 许凯
    2019-12-09
    变更通知时,如果使用长连接,但实际没有送达怎么办呢?要对应答做个标记,再做个定时检查标记吗

    作者回复: 一般会长连短连结合的方式,短连定期轮询,长连负责推送

    4
  • Geek_6303de
    2022-03-17
    居然没有说到nacos嘛
    共 1 条评论
    3
  • 2020-06-19
    有个小疑问,像京东这类网站,通过web访问,页面这些静态资源肯定是放在类似tomact这类的web容器,用户量肯定好多亿,前端是如何负载均衡的

    作者回复: 静态资源在cdn上,不会经过web服务器

    2
  • 何磊
    2019-12-09
    使用配置中心遇到的几个问题 1. 配置中心也是keyvalue的方式存储或者说返回,需要按照服务纬度拉取配置,所以本地还是要在配置文件中写明要拉取配置的服务名 2. 我们实际操作中将配置中心跟注册中心整合了,本地配置记录服务名,注册中心不仅记录服务的node还记录相关配置。比如:db的账号密码。如果是只有配置也可以用这种方式,该方式对于agent维护成本降低
    展开

    作者回复: 👍

    共 2 条评论
    2
  • 被秒
    2019-12-09
    我个人更喜欢在环境变量的方式读配置,毕竟无宕机的rolling update的实现已经是标配了。毕竟引入一个重量级的配置中心的就引入了复杂度, 尤其是那些需要侵入的方式。
    2
  • aoe
    2020-09-28
    波波老师也推荐过Apollo
    共 2 条评论
    1
  • 古德
    2020-03-22
    除了短连接定时轮循,和长连接的方式之外。是不是还有一种long-polling的长轮询方式,也可以用在配置中心这个场景里面

    作者回复: 是的,也是可以的

    1
  • 知行合一
    2019-12-09
    配置中心怎么才能实现6个9的系统呢?

    作者回复: 主要是做好容灾

    1
  • 看不到de颜色
    2022-10-24 来自北京
    请教老师一个问题:用MD5判断值是否变更会不会存在极端情况下值发生了细微改变但是MD5并没有变。MD5我印象里只是摘要,是存在不同值但是相同摘要的情况。
  • YANg
    2022-07-13
    好像现在比较火的是Consul
  • sugar
    2022-06-11
    只用过nacos做过配置中心,挺方便
  • 小胡
    2021-09-02
    数据库,HTTP服务得地址等如何使用Apollo实现热更新呢
  • Geek_441001
    2021-05-26
    也就是说为了读取配置中心的数据,还需要额外维护一个配置客户端,然后由应用程序调用配置客户端。请问直接用应用程序调用配置中心有什么问题呢?目前我们只是简单的将全局配置缓存到redis中,然后应用程序调用redis读取配置。
  • William Ning
    2021-05-13
    一旦配置中心发生故障,那么应用程序就会优先使用文件中的配置,这样虽然无法得到配置的变更消息(因为配置中心已经宕机了),但是应用程序还是可以启动起来的,算是一种降级的方案。 不应该是读取应用程序内存中的配置吗?
  • 指尖以东
    2020-06-01
    在k8s里面本地文件还需要挂载,不然就丢了

    作者回复: 是的

    共 2 条评论
  • 2020-05-09
    目前的公司使用的是Apollo,之前的公司是自研的UCC基于ZK实现,两者相比Apollo的使用体验比较差。当然,原理都类似把需要动态调整的配置集中管理起来,不必重启机器就能改变相关配置,这样相当于可以改变程序的运行逻辑,这种需求非常强烈。 配置分级:按应用维度、机房维度、分组维度、机器维度,这种方式一是节省存储空间,另外对于一次修改多台机器的配置也非常的方便,实际上按组划分是比较OK的,因为同组内配置应该一致,不同组应该有所不同 配置分类:这个我觉得也比较重要,根据自己的经验调整各种程序逻辑开关配置,以及日志级别配置是最常见的,也存在调整服务别名的情况,不过比较少,针对常常会动态调整的配置分类,能更加简单容易的控制配置。 配置中心的易用性:这个对于使用非常的重要,不过个人觉得目前公司做的并不好,使用体验比较差。这种东西最好比较傻瓜式,之上是界面化的,控制起来符合常规逻辑,清晰明了最好。
    展开