Go 配置放文件还是环境变量,我踩坑后定了
上周我改了一个配置值,改完重启,服务还是老样子。查了半天才发现,我改的是仓库里的模板文件,运行时读的是另一个路径下的文件。那一刻我下定决心把 Go 服务配置用文件还是环境变量这件事定死。
结论先给:会随部署环境变的走环境变量,属于业务规则的放文件。混着放,就会变成我上面那样。
一、我踩的坑:同一份配置写在了三个地方
那个配置一共散在三处:代码里的默认值、仓库里的模板文件、部署机器上的实际文件。三处谁生效,取决于启动顺序。
更麻烦的是文档。文档写的是模板文件里的值,跟线上跑的不是一回事,新人照着文档改必然出错。
配置问题的本质不是放哪,是有几份真源。只要不止一份,早晚会踩到。
二、判断规则:Go 服务配置用文件还是环境变量
我现在只用一条线来分。
跟环境绑定的,走环境变量:监听端口、数据库地址、密钥、开关。这些东西在本地和线上本来就不一样,写进文件只会互相污染。
属于业务规则的,放文件:限流阈值、默认分页条数、各种枚举选项。这些值在所有环境里一致,放在文件里能进版本管理,改动能被 review 看到。
一个值到底算哪种,问自己「换台机器要不要改」。要改就是环境变量。
三、三种常见做法的取舍
除了纯文件和纯环境变量,还有一种混合做法:文件里放默认值,环境变量覆盖。
它的好处是本地起服务不用配任何东西,坏处是「这个值现在到底是多少」得看两处才知道。我自己的项目用过一段时间,最后退回了纯环境变量,原因就是排查问题时多了一层。
单人项目里,少一层间接比多一份方便更值钱。
四、什么时候才考虑热加载
热加载听着有用,我现在的判断是大多数单人项目不需要。
原因是:配置改了不重启,就得处理「改到一半读到半份配置」的问题,还要管并发读写。这部分复杂度远大于它省下的那点重启时间。
只有一种情况例外——某个开关需要在不重启的前提下紧急关掉功能。这种我会单独留一个开关,而不是把整套配置都做成可热加载。自动化程度要匹配真实需求,不能为了好看。
五、定下来之后的写法
我现在只有两份真源:一份默认值写在代码里,保证空环境也能跑起来;一份部署时的环境变量,由部署脚本注入。
启动的时候做一次校验,缺关键项直接退出,别带着半份配置起来。这一条帮我省了很多排查时间,宁可起不来,也别带着错配置跑。
本地开发我用的是一份不入库的本地环境文件,字段名跟线上完全一样。这样至少不会出现「本地能跑线上不能跑」。走到这一步,Go 服务配置用文件还是环境变量对我来说就只剩一条判断线了。
常见问题
Q:Go 服务配置用文件还是环境变量,密钥怎么存?
A:密钥走环境变量,不要进仓库。如果部署环境支持密钥管理,就交给它注入,代码里只读不写。
Q:本地开发每次都要配一堆环境变量,太麻烦怎么办?
A:用一份不入库的本地文件,字段名和线上完全一致。这样切换环境不用改代码,也不会把本地值带进仓库。
Q:配置文件用什么格式好?
A:我自己优先选结构简单、不用引额外依赖的格式。格式本身不影响正确性,能一眼看清层级和默认值就够了。
相关阅读
本文作者西米,37 岁程序员,一个人加五个 AI 员工在跑一家公司。踩过的坑和真实账本都写在公众号「西米分享」。
完整代码、踩坑清单与最新进展,都在公众号。
📱 关注公众号「西米分享」