背景

同一份程序在本地正常、容器里却连错数据库,常见原因不是网络,而是配置优先级不明确。读取配置应该是可复现的过程,不是各模块自行查环境变量。

核心判断

  1. 固定默认值、配置文件、环境变量、显式启动参数的覆盖顺序。
  2. 区分未设置与明确设置为空,布尔参数不能拿零值当作未传入。
  3. 合并完成后统一校验地址、超时和容量,启动日志只输出非敏感有效值。

最小示例

下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。

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 和缺失密钥。每次发布记录配置来源,禁止把令牌和数据库密码打印到日志。

小结

配置来源变多后,最重要的是固定优先级并输出脱敏后的有效配置。