配置管理:从硬编码到配置中心的演进

作者:晚风信箱 发布时间: 2025-05-14 阅读量:77 评论数:0

我把自己这些年在配置这件事上的折腾按时间顺序排了一下,发现它是一个非常标准的「每一步都解决了上一步的问题,同时制造了新问题」的过程。写下来给还在某一阶段的人做个参考。

阶段零:写在代码里

常量直接躺在源文件里。超时时间 3000、重试三次、批量大小 100。

问题很明显:改一个数字要走完整的发布流程。我们当年为了把一个超时从 3 秒调到 5 秒,走了两小时的发布,改动是一个字符。

但我要为这个阶段说句公道话:大部分常量本来就该待在代码里。一个值如果两年没改过、也没人想改,把它做成可配置只是徒增复杂度,还多一处出错的可能。我现在判断一个值该不该外置,标准是「有没有可能在不发版的情况下需要改它」。

阶段一:属性文件

把值挪到一个配置文件里,跟着包一起发布。改配置还是要发版,但至少值集中了,能一眼看全。

新问题:不同环境的值不一样。于是有了阶段二。

阶段二:按环境分文件

开发一份、测试一份、生产一份,打包时按参数选一份。这个阶段能撑很久,很多中小项目停在这里也没什么不好。

它的坑是三份文件会逐渐分叉。新增一个配置项,开发环境加了,生产环境忘了加,代码里又写了默认值,于是生产悄悄跑在默认值上。我们出过一次事故就是这样:一个开关在测试环境是关的、生产环境因为漏配走了代码里的默认值开着,上线后直接放量。

补救办法我觉得挺有效:启动时校验配置完整性,缺哪一项直接启动失败,别用默认值兜底。启动失败是好事,静默跑错才是灾难。

阶段三:环境变量

容器化之后自然会走到这一步。配置从制品里彻底剥离,同一个镜像在所有环境跑,符合直觉也符合部署模型。

但环境变量有它自己的毛病:全是字符串,没有层级结构(只能用下划线拼),数量一多就没法看,而且一旦要改还是得重启容器。对于那些希望实时生效的开关,这不够用。

阶段四:配置中心

于是上了配置中心,支持在线修改、实时推送、按环境和集群分组、有变更记录。这一步的体验提升是巨大的,尤其是开关类配置——线上出问题,改一个值三秒生效,不用发版。

但它引入的新问题也最多,我踩过这几个:

  • 变更回调里做重活。有人在配置变更的监听函数里重建了整个连接池,还是同步的。一次批量改配置触发多次回调,服务卡了十几秒。回调里应该只做赋值,重的操作扔到别的线程去。
  • 强依赖导致起不来。配置中心抖动的时候,一批实例重启拉不到配置,全部启动失败,故障从「配置查不了」升级成「服务没了」。后来我们加了本地快照:每次拉取成功就落一份到磁盘,拉不到就用快照启动,同时打一个明显的告警。
  • 默认值散落在三个地方。代码里有默认值、配置文件里有一份、配置中心里有一份。到底哪个生效?出问题时得挨个翻。我现在的做法是只保留一处默认值,并且在启动日志里把最终生效的配置全部打印出来(敏感项打掩码)。这行日志救过我无数次。
  • 没人知道谁改的。虽然有变更记录,但半夜出问题时没人会想到去翻。后来我们把配置变更接进了通知群,改一个值全组都能看到,配合时间线排查非常好用。

一点反思

走完这一圈之后,我最大的体会是:配置项的数量本身就是一种成本。每加一个开关,就等于把一条 if 分支从代码搬到了运行时,可能的状态组合翻倍。我们曾经有个服务七十多个配置项,其中至少三十个从上线起就没改过,还有五个我根本不知道是干什么的。

后来我做了一次清理,删掉了四十多项,把已经稳定的开关直接固化进代码。删完之后服务启动日志短了一半,我心里也轻松了一半。

评论