配置管理
问题
Go 项目中如何管理配置?多环境配置怎么处理?
答案
配置来源优先级
命令行参数 > 环境变量 > 配置文件 > 默认值
Viper 实现
import "github.com/spf13/viper"
type Config struct {
Server ServerConfig `mapstructure:"server"`
DB DBConfig `mapstructure:"db"`
Redis RedisConfig `mapstructure:"redis"`
}
type ServerConfig struct {
Port int `mapstructure:"port"`
Timeout time.Duration `mapstructure:"timeout"`
}
func LoadConfig() (*Config, error) {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("./configs")
viper.AddConfigPath(".")
// 环境变量覆盖
viper.AutomaticEnv()
viper.SetEnvPrefix("APP")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
// 默认值
viper.SetDefault("server.port", 8080)
viper.SetDefault("server.timeout", "30s")
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}
# configs/config.yaml
server:
port: 8080
timeout: 30s
db:
dsn: "user:pass@tcp(localhost:3306)/app?parseTime=true"
max_open: 25
max_idle: 5
redis:
addr: "localhost:6379"
多环境配置
# 通过环境变量切换
APP_ENV=production go run ./cmd/server
# 或在代码中
env := os.Getenv("APP_ENV")
viper.SetConfigName("config." + env) // config.production.yaml
纯环境变量方案
适合容器化部署(12-Factor App):
import "github.com/caarlos0/env/v10"
type Config struct {
Port int `env:"PORT" envDefault:"8080"`
DBHost string `env:"DB_HOST,required"`
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
}
func LoadConfig() (*Config, error) {
cfg := &Config{}
return cfg, env.Parse(cfg)
}
常见面试问题
Q1: 配置中不应该出现什么?
答案:
- 密钥/密码:用 Vault 或 K8s Secrets
- 代码逻辑:配置只控制行为参数
- 环境特定的硬编码:IP、端口应走环境变量
Q2: Viper vs 环境变量 vs 配置文件?
答案:
- 本地开发:YAML 配置文件(可读性好)
- Docker/K8s:环境变量(容器化标准)
- 复杂项目:Viper 支持两者兼容 + 热更新