最稳方式是用os.Getenv("ENV")动态拼接viper.SetConfigName("config."+env),而非硬编码;需配合AddConfigPath两次、SetConfigType("yaml")、显式校验必填字段,并生产环境优先用AutomaticEnv()而非YAML。

直接用 os.Getenv("ENV") 读环境变量,再喂给 viper.SetConfigName(),是最稳、最可控的方式。硬编码文件名或在 init() 里加载配置,90% 的线上故障都从这儿开始。
怎么让 viper 加载 config.dev.yaml 或 config.prod.yaml
关键不是“怎么命名文件”,而是“怎么动态决定加载哪个”。viper 本身不认环境,得你告诉它。
- 别写死
viper.SetConfigName("config.dev")—— 部署时改代码等于埋雷 - 统一用
ENV=dev、ENV=prod这类环境变量控制,启动命令像这样:ENV=prod go run main.go - 代码里取值后拼接:
viper.SetConfigName("config." + env),再调viper.ReadInConfig() -
AddConfigPath()至少加两次:当前目录(".")和配置子目录(如"configs/"),避免路径错位导致读不到文件
为什么开发用 YAML、生产千万别默认用 YAML
YAML 在开发阶段确实好写,但上线后它会悄悄咬你一口。
使用ydata-profiling(前身为pandas-profiling)生成全面的数据质量报告,包含相关性分析、缺失值模式和基数检测。导出交互式HTML仪表板和JSON摘要。
-
yes/no、on/off会被 YAML 解析器自动转成布尔值,viper.GetString("redis.addr")可能返回true而不是字符串,连 panic 都不报,只在连 Redis 时静默失败 - 缩进敏感,CI/CD 自动生成配置时容易多一个空格,解析失败却无明确提示
- 生产环境优先走
viper.AutomaticEnv(),配合viper.SetEnvPrefix("APP"),读APP_DB_HOST这类变量 —— K8s ConfigMap、Docker -e、云平台注入都原生支持 - 非要用文件,生产上选 JSON:
viper.SetConfigType("json"),解析快、无隐式类型转换、格式校验严格
配置加载成功 ≠ 配置可用,必须显式校验字段
viper.ReadInConfig() == nil 只说明文件存在且语法合法,不代表你代码里要的字段都填了。
- 典型翻车现场:
viper.GetString("database.url")返回空字符串,服务照常启动,直到第一个 DB 查询才 panic - 启动时加一层校验逻辑,比如:
if viper.GetString("database.url") == "" { log.Fatal("missing required config: database.url") } - 所有必填字段都要检查,尤其跨环境差异大的项:Redis 地址、第三方 API Key、日志级别、监听端口
- 测试环境也要跑同一套校验,否则本地能过、CI 失败、上线炸锅
最容易被忽略的是:配置字段名和结构体 tag 必须完全对齐,mapstructure:"db_host" 和 viper.GetString("db.host") 是两码事 —— 中间那个点是 viper 的层级分隔符,不是结构体字段名的一部分。

















