Viper 配置管理
Viper 是 Go 语言最流行的配置管理库,支持多种配置格式和环境变量。
📋 学习目标
- 理解配置管理的重要性
- 掌握 Viper 的基本使用
- 学会读取多种配置格式
- 理解环境变量和命令行参数
- 掌握配置热加载
- 了解配置最佳实践
🎯 Viper 简介
为什么选择 Viper
- 多格式支持: JSON、YAML、TOML、环境变量等
- 优先级管理: 支持配置优先级
- 热加载: 支持配置文件变更监听
- 类型安全: 支持类型转换和验证
- 易于使用: API 简洁直观
安装 Viper
bash
go get github.com/spf13/viper🚀 快速开始
基本使用
go
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
// 设置配置文件名(不含扩展名)
viper.SetConfigName("config")
// 设置配置文件类型
viper.SetConfigType("yaml")
// 添加配置文件搜索路径
viper.AddConfigPath(".")
viper.AddConfigPath("./config")
viper.AddConfigPath("$HOME/.myapp")
// 读取配置文件
if err := viper.ReadInConfig(); err != nil {
panic(fmt.Errorf("读取配置文件失败: %w", err))
}
// 获取配置值
port := viper.GetInt("server.port")
fmt.Printf("服务器端口: %d\n", port)
}📝 配置文件格式
YAML 配置
yaml
# config.yaml
server:
port: 8080
host: localhost
timeout: 30s
database:
host: localhost
port: 3306
username: root
password: password
database: mydb
max_connections: 100
redis:
host: localhost
port: 6379
password: ""
db: 0
app:
name: "My Application"
version: "1.0.0"
debug: trueJSON 配置
json
{
"server": {
"port": 8080,
"host": "localhost",
"timeout": "30s"
},
"database": {
"host": "localhost",
"port": 3306,
"username": "root",
"password": "password",
"database": "mydb"
}
}TOML 配置
toml
[server]
port = 8080
host = "localhost"
timeout = "30s"
[database]
host = "localhost"
port = 3306
username = "root"
password = "password"
database = "mydb"🔧 读取配置
基本读取
go
// 读取字符串
name := viper.GetString("app.name")
// 读取整数
port := viper.GetInt("server.port")
// 读取布尔值
debug := viper.GetBool("app.debug")
// 读取时间
timeout := viper.GetDuration("server.timeout")
// 读取字符串切片
tags := viper.GetStringSlice("app.tags")
// 读取映射
database := viper.GetStringMap("database")嵌套配置
go
// 读取嵌套配置
port := viper.GetInt("server.port")
host := viper.GetString("server.host")
// 或使用子配置
serverConfig := viper.Sub("server")
if serverConfig != nil {
port := serverConfig.GetInt("port")
host := serverConfig.GetString("host")
}默认值
go
// 设置默认值
viper.SetDefault("server.port", 8080)
viper.SetDefault("server.host", "localhost")
viper.SetDefault("app.debug", false)
// 读取配置(如果不存在则使用默认值)
port := viper.GetInt("server.port")🌍 环境变量
自动绑定环境变量
go
// 自动读取环境变量
viper.AutomaticEnv()
// 读取环境变量(优先级高于配置文件)
port := viper.GetInt("SERVER_PORT")手动绑定环境变量
go
// 绑定环境变量
viper.BindEnv("server.port", "SERVER_PORT")
viper.BindEnv("server.host", "SERVER_HOST")
viper.BindEnv("database.password", "DB_PASSWORD")
// 读取
port := viper.GetInt("server.port")环境变量前缀
go
// 设置环境变量前缀
viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv()
// 读取 MYAPP_SERVER_PORT
port := viper.GetInt("server.port")🎛️ 命令行参数
绑定命令行标志
go
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func init() {
// 绑定命令行参数
viper.BindPFlag("server.port", rootCmd.Flags().Lookup("port"))
viper.BindPFlag("server.host", rootCmd.Flags().Lookup("host"))
}
var rootCmd = &cobra.Command{
Use: "myapp",
Run: func(cmd *cobra.Command, args []string) {
port := viper.GetInt("server.port")
fmt.Printf("端口: %d\n", port)
},
}
func init() {
rootCmd.Flags().IntP("port", "p", 8080, "服务器端口")
rootCmd.Flags().StringP("host", "H", "localhost", "服务器地址")
}🔄 配置热加载
监听配置文件
go
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
// 监听配置文件变化
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("配置文件已更新:", e.Name)
// 重新读取配置
port := viper.GetInt("server.port")
fmt.Printf("新端口: %d\n", port)
})
// 保持程序运行
select {}
}🏃♂️ 实践应用
完整的配置管理示例
go
package config
import (
"fmt"
"github.com/spf13/viper"
)
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfig `mapstructure:"database"`
Redis RedisConfig `mapstructure:"redis"`
App AppConfig `mapstructure:"app"`
}
type ServerConfig struct {
Port int `mapstructure:"port"`
Host string `mapstructure:"host"`
Timeout string `mapstructure:"timeout"`
}
type DatabaseConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Database string `mapstructure:"database"`
MaxConns int `mapstructure:"max_connections"`
}
type RedisConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Password string `mapstructure:"password"`
DB int `mapstructure:"db"`
}
type AppConfig struct {
Name string `mapstructure:"name"`
Version string `mapstructure:"version"`
Debug bool `mapstructure:"debug"`
}
var AppConfigInstance *Config
func LoadConfig(configPath string) (*Config, error) {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
if configPath != "" {
viper.SetConfigFile(configPath)
} else {
viper.AddConfigPath(".")
viper.AddConfigPath("./config")
viper.AddConfigPath("$HOME/.myapp")
}
// 设置默认值
setDefaults()
// 读取环境变量
viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv()
// 绑定环境变量
bindEnvVars()
// 读取配置文件
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return nil, fmt.Errorf("读取配置文件失败: %w", err)
}
}
var config Config
if err := viper.Unmarshal(&config); err != nil {
return nil, fmt.Errorf("解析配置失败: %w", err)
}
AppConfigInstance = &config
return &config, nil
}
func setDefaults() {
viper.SetDefault("server.port", 8080)
viper.SetDefault("server.host", "localhost")
viper.SetDefault("server.timeout", "30s")
viper.SetDefault("database.host", "localhost")
viper.SetDefault("database.port", 3306)
viper.SetDefault("database.max_connections", 100)
viper.SetDefault("app.debug", false)
}
func bindEnvVars() {
viper.BindEnv("server.port", "MYAPP_SERVER_PORT")
viper.BindEnv("server.host", "MYAPP_SERVER_HOST")
viper.BindEnv("database.host", "MYAPP_DB_HOST")
viper.BindEnv("database.password", "MYAPP_DB_PASSWORD")
viper.BindEnv("app.debug", "MYAPP_DEBUG")
}
// 使用示例
func main() {
config, err := LoadConfig("")
if err != nil {
panic(err)
}
fmt.Printf("服务器: %s:%d\n", config.Server.Host, config.Server.Port)
fmt.Printf("数据库: %s:%d\n", config.Database.Host, config.Database.Port)
}⚠️ 注意事项
1. 配置优先级
go
// 优先级从高到低:
// 1. 显式调用 Set
// 2. 命令行标志
// 3. 环境变量
// 4. 配置文件
// 5. 默认值2. 类型转换
go
// Viper 会自动进行类型转换
port := viper.GetInt("server.port") // 字符串 "8080" 转换为 int
timeout := viper.GetDuration("timeout") // 字符串 "30s" 转换为 time.Duration3. 配置验证
go
// 验证必需的配置项
requiredKeys := []string{"server.port", "database.host"}
for _, key := range requiredKeys {
if !viper.IsSet(key) {
return fmt.Errorf("缺少必需的配置项: %s", key)
}
}📚 扩展阅读
⏭️ 下一章节
Zap 日志库 → 学习结构化日志
💡 提示: Viper 是 Go 项目中最常用的配置管理库,掌握它可以让配置管理变得简单高效!
