Go 配置分层:默认值、环境变量和启动参数各司其职
背景 同一份程序在本地正常、容器里却连错数据库,常见原因不是网络,而是配置优先级不明确。读取配置应该是可复现的过程,不是各模块自行查环境变量。 核心判断 固定默认值、配置文件、环境变量、显式启动参数的覆盖顺序。 区分未设置与明确设置为空,布尔参数不能拿零值当作未传入。 合并完成后统一校验地址、超时和容量,启动日志只输出非敏感有效值。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 type Config struct { Addr string Timeout time.Duration Debug bool } func Load() (Config, error) { cfg := Config{Addr: ":8080", Timeout: 5 * time.Second} if err := applyEnv(&cfg); err != nil { return Config{}, err } if err := applyFlags(&cfg); err != nil { return Config{}, err } if cfg.Addr == "" || cfg.Timeout <= 0 { return Config{}, fmt.Errorf("invalid address or timeout") } return cfg, nil } 边界与误区 解析失败不能悄悄退回默认值;否则错误的超时配置会被伪装成启动成功。示例只展示合并入口,applyEnv、applyFlags 应实现转换、错误返回和显式参数检测。 验证方式 用表驱动测试覆盖多来源冲突、空值、非法 duration 和缺失密钥。每次发布记录配置来源,禁止把令牌和数据库密码打印到日志。 小结 配置来源变多后,最重要的是固定优先级并输出脱敏后的有效配置。