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 次