配置管理

Viper 是 Go 配置管理库,支持默认值、配置文件、环境变量、命令行参数和远程配置等来源。

go get github.com/spf13/viper

读取配置

建议创建独立实例,避免全局状态影响测试和多个配置模块。

type Config struct {
    Server struct {
        Port int `mapstructure:"port"`
    } `mapstructure:"server"`
    DatabaseURL string `mapstructure:"database_url"`
}

v := viper.New()
v.SetConfigName("config")
v.SetConfigType("yaml")
v.AddConfigPath(".")

v.SetDefault("server.port", 8080)
v.SetEnvPrefix("APP")
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
v.AutomaticEnv()
v.BindEnv("database_url")

if err := v.ReadInConfig(); err != nil {
    var notFound viper.FileLookupError
    if !errors.As(err, &notFound) {
        return Config{}, err
    }
}

var config Config
if err := v.Unmarshal(&config); err != nil {
    return Config{}, err
}

对应的 config.yaml

server:
  port: 8080
database_url: postgres://localhost/app

环境变量 APP_DATABASE_URL 会覆盖配置文件中的 database_url。Viper 的优先级从高到低为:显式 Set、命令行参数、环境变量、配置文件、远程配置和默认值。

绑定命令行参数

Viper 可以直接绑定 Cobra 使用的 pflag

flags.Int("port", 8080, "HTTP server port")
v.BindPFlag("server.port", flags.Lookup("port"))

绑定操作不会立即读取参数值,调用 GetUnmarshal 时才会解析最终配置。

实践建议

  • 读取完成后映射到结构体,再把 Config 注入业务组件,不要让业务代码到处调用 viper.Get
  • 显式绑定需要覆盖的环境变量,避免环境变量存在但未进入 Unmarshal 结果。
  • 密码和 Token 不应提交到配置文件,应通过环境变量或密钥管理系统注入。
  • 动态监听配置前先确认组件是否支持并发更新;数据库连接等资源通常不能只修改字段就完成热更新。
  • Viper 不会深度合并复杂配置,某个嵌套值被覆盖时,整个值可能被替换。