Viper配置管理
2025/12/23大约 7 分钟
Viper 是 Go 最流行的配置管理库,支持多种配置格式和环境变量。本文通过完整可运行的示例,并结合 Spring Boot 配置对比,帮助你快速掌握 Viper 使用。
Viper 配置管理:YAML、环境变量、多环境配置
Java 对比:Viper 类似 Spring Boot 的
application.yml+@ConfigurationProperties。
一、项目初始化
1.1 创建项目
cd ~/GolandProjects
mkdir go-viper-demo && cd go-viper-demo
go mod init go-viper-demo
# 安装 Viper(指定兼容版本)
go get github.com/spf13/viper@v1.18.2版本说明:
- 所有示例代码兼容 Go 1.18-1.22 版本
- Viper v1.18.2 已在 Go 1.22.2 上测试通过
- 如需使用最新版本:
go get -u github.com/spf13/viper
二、基础配置
2.1 读取 YAML 配置
创建配置文件 config.yaml:
nano config.yaml
# 将以下配置复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出app:
name: MyApp
version: 1.0.0
port: 8080
database:
host: localhost
port: 3306
username: root
password: password
dbname: testdb
redis:
host: localhost
port: 6379
password: ""
db: 0创建 viper_basic.go:
nano viper_basic.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
// 1. 设置配置文件名和路径
viper.SetConfigName("config") // 配置文件名(不含扩展名)
viper.SetConfigType("yaml") // 配置文件类型
viper.AddConfigPath(".") // 配置文件路径
// 2. 读取配置文件
if err := viper.ReadInConfig(); err != nil {
panic(fmt.Errorf("Fatal error config file: %s", err))
}
fmt.Println("Config file loaded successfully!")
// 3. 读取单个配置项
appName := viper.GetString("app.name")
appPort := viper.GetInt("app.port")
fmt.Printf("App Name: %s\n", appName)
fmt.Printf("App Port: %d\n", appPort)
// 4. 读取嵌套配置
dbHost := viper.GetString("database.host")
dbPort := viper.GetInt("database.port")
fmt.Printf("Database: %s:%d\n", dbHost, dbPort)
// 5. 读取所有配置
allSettings := viper.AllSettings()
fmt.Printf("All settings: %v\n", allSettings)
}运行 & 测试
go run viper_basic.go
# Config file loaded successfully!
# App Name: MyApp
# App Port: 8080
# Database: localhost:3306
# All settings: map[app:map[name:MyApp port:8080 version:1.0.0] database:map[dbname:testdb host:localhost password:password port:3306 username:root] redis:map[db:0 host:localhost password: port:6379]]Java 对比:
viper.GetString()≈@Value("${app.name}")- YAML 配置 ≈ Spring Boot 的
application.yml
三、结构体绑定
3.1 将配置映射到结构体
创建 viper_struct.go:
nano viper_struct.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"github.com/spf13/viper"
)
// 配置结构体
type Config struct {
App AppConfig `mapstructure:"app"`
Database DatabaseConfig `mapstructure:"database"`
Redis RedisConfig `mapstructure:"redis"`
}
type AppConfig struct {
Name string `mapstructure:"name"`
Version string `mapstructure:"version"`
Port int `mapstructure:"port"`
}
type DatabaseConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
DBName string `mapstructure:"dbname"`
}
type RedisConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Password string `mapstructure:"password"`
DB int `mapstructure:"db"`
}
func main() {
// 读取配置文件
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
// 将配置绑定到结构体
var config Config
if err := viper.Unmarshal(&config); err != nil {
panic(err)
}
// 使用配置
fmt.Printf("App: %s v%s\n", config.App.Name, config.App.Version)
fmt.Printf("Port: %d\n", config.App.Port)
fmt.Printf("Database: %s@%s:%d/%s\n",
config.Database.Username,
config.Database.Host,
config.Database.Port,
config.Database.DBName,
)
fmt.Printf("Redis: %s:%d (DB %d)\n",
config.Redis.Host,
config.Redis.Port,
config.Redis.DB,
)
}运行 & 测试
go run viper_struct.go
# App: MyApp v1.0.0
# Port: 8080
# Database: root@localhost:3306/testdb
# Redis: localhost:6379 (DB 0)Java 对比:
viper.Unmarshal()≈@ConfigurationProperties(prefix = "app")mapstructuretag ≈@Value注解
四、环境变量
4.1 绑定环境变量
创建 viper_env.go:
nano viper_env.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func main() {
// 1. 设置环境变量前缀
viper.SetEnvPrefix("MYAPP") // 自动添加前缀
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
// 2. 绑定环境变量
viper.BindEnv("port") // 绑定 MYAPP_PORT
viper.BindEnv("debug") // 绑定 MYAPP_DEBUG
viper.BindEnv("db.host") // 绑定 MYAPP_DB_HOST
// 3. 设置默认值
viper.SetDefault("port", 8080)
viper.SetDefault("debug", false)
// 4. 设置环境变量(模拟)
os.Setenv("MYAPP_PORT", "9000")
os.Setenv("MYAPP_DEBUG", "true")
os.Setenv("MYAPP_DB_HOST", "192.168.1.100")
// 5. 读取配置(优先级:环境变量 > 配置文件 > 默认值)
port := viper.GetInt("port")
debug := viper.GetBool("debug")
dbHost := viper.GetString("db.host")
fmt.Printf("Port: %d (from env)\n", port)
fmt.Printf("Debug: %v (from env)\n", debug)
fmt.Printf("DB Host: %s (from env)\n", dbHost)
// 6. 自动绑定所有环境变量
viper.AutomaticEnv()
os.Setenv("MYAPP_CUSTOM_KEY", "custom_value")
customKey := viper.GetString("custom.key") // 自动转换 CUSTOM_KEY -> custom.key
fmt.Printf("Custom Key: %s\n", customKey)
}运行 & 测试
go run viper_env.go
# Port: 9000 (from env)
# Debug: true (from env)
# DB Host: 192.168.1.100 (from env)
# Custom Key: custom_valueJava 对比:
viper.BindEnv()≈${ENV_VAR}viper.AutomaticEnv()≈ Spring Boot 的自动环境变量绑定
五、多环境配置
5.1 根据环境加载不同配置
创建配置文件:
config.dev.yaml(开发环境):
nano config.dev.yaml
# 将以下配置复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出app:
name: MyApp-Dev
port: 8080
debug: true
database:
host: localhost
port: 3306
username: root
password: password
dbname: dev_dbconfig.prod.yaml(生产环境):
nano config.prod.yaml
# 将以下配置复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出app:
name: MyApp-Prod
port: 80
debug: false
database:
host: prod.database.com
port: 3306
username: prod_user
password: prod_password
dbname: prod_db创建 viper_multi_env.go:
nano viper_multi_env.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
type Config struct {
App struct {
Name string `mapstructure:"name"`
Port int `mapstructure:"port"`
Debug bool `mapstructure:"debug"`
} `mapstructure:"app"`
Database struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
DBName string `mapstructure:"dbname"`
} `mapstructure:"database"`
}
func LoadConfig(env string) (*Config, error) {
// 根据环境加载配置文件
configName := fmt.Sprintf("config.%s", env)
viper.SetConfigName(configName)
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
var config Config
if err := viper.Unmarshal(&config); err != nil {
return nil, err
}
return &config, nil
}
func main() {
// 从环境变量读取环境名称
env := os.Getenv("APP_ENV")
if env == "" {
env = "dev" // 默认开发环境
}
fmt.Printf("Loading config for environment: %s\n", env)
config, err := LoadConfig(env)
if err != nil {
panic(err)
}
fmt.Printf("App: %s\n", config.App.Name)
fmt.Printf("Port: %d\n", config.App.Port)
fmt.Printf("Debug: %v\n", config.App.Debug)
fmt.Printf("Database: %s@%s:%d/%s\n",
config.Database.Username,
config.Database.Host,
config.Database.Port,
config.Database.DBName,
)
}运行 & 测试
# 开发环境
APP_ENV=dev go run viper_multi_env.go
# Loading config for environment: dev
# App: MyApp-Dev
# Port: 8080
# Debug: true
# Database: root@localhost:3306/dev_db
# 生产环境
APP_ENV=prod go run viper_multi_env.go
# Loading config for environment: prod
# App: MyApp-Prod
# Port: 80
# Debug: false
# Database: prod_user@prod.database.com:3306/prod_dbJava 对比:
- 多环境配置 ≈ Spring Boot 的
application-{profile}.ymlAPP_ENV≈spring.profiles.active
六、配置热重载
6.1 监听配置文件变化
创建 viper_watch.go:
nano viper_watch.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"time"
"github.com/spf13/viper"
)
func main() {
// 读取配置文件
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
fmt.Println("Initial config loaded")
fmt.Printf("App Name: %s\n", viper.GetString("app.name"))
fmt.Printf("App Port: %d\n", viper.GetInt("app.port"))
// 监听配置文件变化
viper.WatchConfig()
viper.OnConfigChange(func(e interface{}) {
fmt.Println("\n--- Config file changed! ---")
fmt.Printf("App Name: %s\n", viper.GetString("app.name"))
fmt.Printf("App Port: %d\n", viper.GetInt("app.port"))
})
fmt.Println("\nWatching for config changes... (modify config.yaml to see changes)")
fmt.Println("Press Ctrl+C to exit")
// 保持程序运行
select {}
}运行 & 测试
go run viper_watch.go
# Initial config loaded
# App Name: MyApp
# App Port: 8080
#
# Watching for config changes... (modify config.yaml to see changes)
# Press Ctrl+C to exit
# 修改 config.yaml 后自动输出:
# --- Config file changed! ---
# App Name: MyApp-Updated
# App Port: 9000Java 对比:
viper.WatchConfig()≈ Spring Cloud Config 的@RefreshScope- 热重载 ≈ Spring Boot DevTools
七、支持的配置格式
7.1 JSON 配置
创建 config.json:
nano config.json
# 将以下配置复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出{
"app": {
"name": "MyApp",
"port": 8080
},
"database": {
"host": "localhost",
"port": 3306
}
}创建 viper_json.go:
nano viper_json.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigName("config")
viper.SetConfigType("json")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
fmt.Printf("App Name: %s\n", viper.GetString("app.name"))
fmt.Printf("App Port: %d\n", viper.GetInt("app.port"))
}7.2 TOML 配置
创建 config.toml:
nano config.toml
# 将以下配置复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出[app]
name = "MyApp"
port = 8080
[database]
host = "localhost"
port = 3306创建 viper_toml.go:
nano viper_toml.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigName("config")
viper.SetConfigType("toml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
fmt.Printf("App Name: %s\n", viper.GetString("app.name"))
fmt.Printf("App Port: %d\n", viper.GetInt("app.port"))
}八、配置优先级
Viper 的配置优先级(从高到低):
- 显式设置:
viper.Set() - 命令行参数:
viper.BindPFlag() - 环境变量:
viper.BindEnv() - 配置文件:
viper.ReadInConfig() - Key/Value 存储:etcd, Consul
- 默认值:
viper.SetDefault()
创建 viper_priority.go:
nano viper_priority.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func main() {
// 1. 设置默认值(优先级最低)
viper.SetDefault("port", 8080)
fmt.Printf("Default: %d\n", viper.GetInt("port"))
// 2. 读取配置文件(优先级中等)
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.ReadInConfig()
fmt.Printf("From config file: %d\n", viper.GetInt("app.port"))
// 3. 环境变量(优先级较高)
viper.BindEnv("port")
os.Setenv("PORT", "9000")
fmt.Printf("From env: %d\n", viper.GetInt("port"))
// 4. 显式设置(优先级最高)
viper.Set("port", 10000)
fmt.Printf("Explicit set: %d\n", viper.GetInt("port"))
}运行 & 测试
go run viper_priority.go
# Default: 8080
# From config file: 8080
# From env: 9000
# Explicit set: 10000九、对比总结
| 功能 | Viper | Spring Boot | 说明 |
|---|---|---|---|
| 配置文件 | YAML/JSON/TOML | YAML/Properties | Viper 支持更多格式 |
| 结构体绑定 | Unmarshal | @ConfigurationProperties | 类似 |
| 环境变量 | BindEnv | ${ENV_VAR} | Viper 更灵活 |
| 多环境 | 多配置文件 | Profiles | 实现方式不同 |
| 热重载 | WatchConfig | @RefreshScope | Viper 内置支持 |
| 优先级 | 6 层优先级 | 多层覆盖 | 类似 |
十、常见问题
Q: 如何读取数组/切片配置?
A:
// config.yaml: items: [a, b, c]
items := viper.GetStringSlice("items")Q: 如何设置配置文件的多个搜索路径?
A:
viper.AddConfigPath(".")
viper.AddConfigPath("./config")
viper.AddConfigPath("/etc/myapp")Q: 如何判断配置项是否存在?
A:
if viper.IsSet("app.port") {
port := viper.GetInt("app.port")
}十一、下一步
✅ 已掌握 Viper 配置管理
→ 下一章:Zap 日志系统
→ 再下一章:综合实战项目
祝你编码愉快!🚀
