Skip to content

配置读取 ​

Viper 支持从多种来源读取配置,包括配置文件、环境变量、命令行参数等。

配置文件读取 ​

基础配置读取 ​

go
package main

import (
    "fmt"
    "github.com/spf13/viper"
)

func main() {
    v := viper.New()
    
    v.SetConfigName("config")     // 配置文件名 (无扩展名)
    v.SetConfigType("yaml")       // 配置文件类型
    v.AddConfigPath(".")          // 配置文件路径
    v.AddConfigPath("./config")   // 多个路径
    
    if err := v.ReadInConfig(); err != nil {
        if _, ok := err.(viper.ConfigFileNotFoundError); ok {
            fmt.Println("配置文件未找到")
        } else {
            fmt.Printf("读取配置文件错误: %v\n", err)
        }
    }
    
    fmt.Println(v.GetString("app.name"))
}

支持的配置格式 ​

格式扩展名SetConfigType
YAML.yaml, .ymlyaml
JSON.jsonjson
TOML.tomltoml
HCL.hclhcl
INI.iniini
Properties.propertiesproperties

YAML 配置文件 ​

yaml
app:
  name: myapp
  version: 1.0.0
  port: 8080
  
database:
  host: localhost
  port: 3306
  name: mydb
  user: root
  password: password
  
cache:
  enabled: true
  ttl: 3600

JSON 配置文件 ​

json
{
  "app": {
    "name": "myapp",
    "version": "1.0.0",
    "port": 8080
  },
  "database": {
    "host": "localhost",
    "port": 3306,
    "name": "mydb",
    "user": "root",
    "password": "password"
  }
}

TOML 配置文件 ​

toml
[app]
name = "myapp"
version = "1.0.0"
port = 8080

[database]
host = "localhost"
port = 3306
name = "mydb"
user = "root"
password = "password"

获取配置值 ​

基本类型 ​

go
// 字符串
name := viper.GetString("app.name")

// 整数
port := viper.GetInt("app.port")

// 布尔值
enabled := viper.GetBool("cache.enabled")

// 浮点数
ratio := viper.GetFloat64("cache.ratio")

// 时间
timeout := viper.GetDuration("http.timeout")

// 时间戳
time := viper.GetTime("app.created_at")

// 字节数组
data := viper.GetBytes("app.secret")

复杂类型 ​

go
// 字符串数组
hosts := viper.GetStringSlice("database.hosts")

// 整数数组
ports := viper.GetIntSlice("database.ports")

// 字符串映射
tags := viper.GetStringMap("app.tags")

// 字符串映射字符串
envs := viper.GetStringMapString("app.envs")

// 任意类型
value := viper.Get("app.config")

获取嵌套配置 ​

go
// 使用点号访问嵌套配置
dbHost := viper.GetString("database.host")
dbPort := viper.GetInt("database.port")

// 多层嵌套
maxConn := viper.GetInt("database.connection.max")

环境变量读取 ​

自动读取环境变量 ​

go
viper.AutomaticEnv()

// 环境变量 APP_NAME 对应 app.name
name := viper.GetString("app.name")

设置环境变量前缀 ​

go
viper.SetEnvPrefix("APP")

// 环境变量 APP_PORT 对应 port
port := viper.GetInt("port")

自定义环境变量绑定 ​

go
viper.BindEnv("database.host", "DB_HOST")
viper.BindEnv("database.port", "DB_PORT")

host := viper.GetString("database.host")

环境变量替换 ​

go
// 使用环境变量替换配置值
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

// app.name -> APP_NAME
name := viper.GetString("app.name")

环境变量示例 ​

bash
export APP_NAME=myapp
export APP_PORT=8080
export DB_HOST=localhost
export DB_PORT=3306

命令行参数读取 ​

使用 pflag ​

go
import (
    "github.com/spf13/pflag"
    "github.com/spf13/viper"
)

func main() {
    pflag.String("config", "config.yaml", "config file path")
    pflag.String("port", "8080", "server port")
    pflag.Parse()
    
    viper.BindPFlags(pflag.CommandLine)
    
    config := viper.GetString("config")
    port := viper.GetInt("port")
}

使用 flag ​

go
import (
    "flag"
    "github.com/spf13/viper"
)

func main() {
    flag.String("config", "config.yaml", "config file path")
    flag.Int("port", 8080, "server port")
    flag.Parse()
    
    viper.BindFlagValues(flag.CommandLine)
    
    config := viper.GetString("config")
    port := viper.GetInt("port")
}

默认值设置 ​

设置单个默认值 ​

go
viper.SetDefault("app.name", "myapp")
viper.SetDefault("app.port", 8080)
viper.SetDefault("database.host", "localhost")
viper.SetDefault("database.port", 3306)

设置嵌套默认值 ​

go
viper.SetDefault("database.connection.max", 100)
viper.SetDefault("database.connection.timeout", "30s")

使用配置结构体 ​

go
type Config struct {
    App struct {
        Name    string `mapstructure:"name"`
        Port    int    `mapstructure:"port"`
    } `mapstructure:"app"`
    
    Database struct {
        Host string `mapstructure:"host"`
        Port int    `mapstructure:"port"`
    } `mapstructure:"database"`
}

func main() {
    viper.SetConfigFile("config.yaml")
    viper.ReadInConfig()
    
    var config Config
    viper.Unmarshal(&config)
    
    fmt.Println(config.App.Name)
    fmt.Println(config.Database.Host)
}

远程配置读取 ​

Consul 配置中心 ​

go
import (
    "github.com/spf13/viper"
    "github.com/spf13/viper/remote"
)

func main() {
    viper.AddRemoteProvider("consul", "localhost:8500", "/config/myapp")
    viper.SetConfigType("yaml")
    
    if err := viper.ReadRemoteConfig(); err != nil {
        log.Fatal(err)
    }
    
    name := viper.GetString("app.name")
}

Etcd 配置中心 ​

go
viper.AddRemoteProvider("etcd", "http://localhost:2379", "/config/myapp")
viper.SetConfigType("yaml")
viper.ReadRemoteConfig()

Firestore 配置中心 ​

go
viper.AddRemoteProvider("firestore", "https://firestore.googleapis.com", "config/myapp")
viper.SetConfigType("json")
viper.ReadRemoteConfig()

多配置源优先级 ​

优先级顺序 ​

1. 命令行参数 (最高)
2. 环境变量
3. 配置文件
4. 默认值 (最低)

配置覆盖示例 ​

go
func main() {
    // 1. 设置默认值
    viper.SetDefault("app.port", 8080)
    
    // 2. 读取配置文件
    viper.SetConfigFile("config.yaml")
    viper.ReadInConfig()
    
    // 3. 读取环境变量
    viper.AutomaticEnv()
    
    // 4. 读取命令行参数
    pflag.Int("port", 8080, "server port")
    pflag.Parse()
    viper.BindPFlags(pflag.CommandLine)
    
    // 最终值取决于最高优先级
    port := viper.GetInt("app.port")
    fmt.Println(port)
}

检查配置是否存在 ​

IsSet 检查 ​

go
if viper.IsSet("app.name") {
    name := viper.GetString("app.name")
}

if viper.IsSet("database") {
    // database 配置存在
}

InConfig 检查 ​

go
// 检查是否在配置文件中
if viper.InConfig("app.name") {
    // app.name 在配置文件中定义
}

// 检查是否在环境变量中
if viper.InConfig("APP_NAME") {
    // APP_NAME 环境变量存在
}

配置搜索路径 ​

添加多个搜索路径 ​

go
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("/etc/myapp")
viper.AddConfigPath("$HOME/.myapp")
viper.AddConfigPath(".")
viper.ReadInConfig()

使用绝对路径 ​

go
viper.SetConfigFile("/etc/myapp/config.yaml")
if err := viper.ReadInConfig(); err != nil {
    log.Fatal(err)
}

配置文件监控 ​

监听配置文件变更 ​

go
viper.OnConfigChange(func(e fsnotify.Event) {
    fmt.Printf("配置文件变更: %s\n", e.Name)
    // 处理配置变更逻辑
})

viper.WatchConfig()

完整示例 ​

go
func main() {
    viper.SetConfigFile("config.yaml")
    viper.ReadInConfig()
    
    viper.WatchConfig()
    viper.OnConfigChange(func(e fsnotify.Event) {
        log.Printf("配置文件变更: %s, 操作: %v", e.Name, e.Op)
        
        // 重新加载配置
        if err := viper.ReadInConfig(); err != nil {
            log.Printf("重新加载配置失败: %v", err)
        }
        
        // 更新应用配置
        updateConfig()
    })
    
    // 启动应用
    startApp()
}

子配置对象 ​

创建子 viper ​

go
sub := viper.Sub("database")
if sub != nil {
    host := sub.GetString("host")
    port := sub.GetInt("port")
}

使用子配置 ​

go
// 获取 database 子配置
subViper := viper.Sub("database")

// 使用子配置读取值
host := subViper.GetString("host")

// 子配置解析为结构体
type DatabaseConfig struct {
    Host string `mapstructure:"host"`
    Port int    `mapstructure:"port"`
}

var dbConfig DatabaseConfig
subViper.Unmarshal(&dbConfig)

最佳实践 ​

  1. 设置默认值:为关键配置设置合理的默认值
  2. 多源组合:配置文件 + 环境变量 + 命令行参数
  3. 优先级理解:了解配置源的优先级顺序
  4. 类型安全:使用结构体 Unmarshal 获取配置
  5. 错误处理:处理配置文件未找到和解析错误
  6. 配置路径:提供多个配置搜索路径
  7. 环境变量:使用 AutomaticEnv 自动读取

参考:https://github.com/spf13/viper