Go 配置管理实战:用 viper 加载多环境配置并支持热重载
test12026-09-176 次阅读
配置不该写死在代码里
开发、测试、生产三套环境的数据库地址、端口、密钥各不相同。把配置散落在常量里,每次换环境都要重新编译。用 viper 统一读取文件、环境变量与命令行参数,并按环境切换,是 Go 服务的标准做法。
基础读取:文件 + 环境变量
viper 支持 JSON、YAML、TOML 等格式,且环境变量优先级天然高于文件,方便在容器里用 ENV 覆盖配置。
package main
import (
"fmt"
"log"
"github.com/spf13/viper"
)
type Config struct {
Port int `mapstructure:"port"`
DBURL string `mapstructure:"db_url"`
LogLevel string `mapstructure:"log_level"`
}
func loadConfig() (Config, error) {
var c Config
viper.SetConfigName("config") // config.yaml
viper.AddConfigPath(".") // 当前目录
viper.AddConfigPath("./configs")
viper.SetConfigType("yaml")
viper.SetDefault("port", 8080)
viper.SetDefault("log_level", "info")
viper.AutomaticEnv() // 允许 ENV 覆盖
if err := viper.ReadInConfig(); err != nil {
return c, err
}
if err := viper.Unmarshal(&c); err != nil {
return c, err
}
return c, nil
}
func main() {
c, err := loadConfig()
if err != nil {
log.Fatal(err)
}
fmt.Printf("listen on :%d, level=%s\n", c.Port, c.LogLevel)
}
多环境切换
用 VIPER 的 SetConfigName 配合环境文件名区分,例如 config.dev.yaml 与 config.prod.yaml,再用环境变量 APP_ENV 选文件。
env := os.Getenv("APP_ENV")
if env == "" {
env = "dev"
}
viper.SetConfigName("config." + env) // config.prod.yaml
热重载:配置变了不用重启
监听 OnConfigChange 事件,文件被修改时自动重新解析。对日志级别、限流阈值这类运行时可调项很实用。
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Println("配置已变更:", e.Name)
if err := viper.Unmarshal(¤t); err != nil {
log.Println("重载失败:", err)
}
})
实践要点
- 敏感字段(密钥、Token)只走环境变量或密钥管理,别进仓库里的配置文件。
- 结构体用 mapstructure tag 映射,字段改名时旧配置仍能兼容。
- 热重载后要校验新配置合法性,非法值回退到旧值并打印告警。
- 多实例部署时,优先用配置中心或 ENV,避免每个节点各自改文件。
T
test1
文章作者
配置不该写死在代码里 开发、测试、生产三套环境的数据库地址、端口、密钥各不相同。把配置散落在常量里,每次换环境都要重新...
- 分类
- 技术
- 发布时间
- 2026-09-17
- 字数
- 约 1628 字
- 阅读
- 6 次
