Go:Viper

时间:May 13, 2020 分类:

目录:

Viper是Golang中用于配置文件的解决方案,可以处理绝大多数类型的配置和一些特性

  • 设置默认值
  • 从json,toml,yaml等格式获取配置信息
  • 从环境变量获取
  • 从远程配置程序(etcd和consul等)读取并监控配置变化
  • 从命令行参数读取配置
  • 实时监控和重新读取配置文件(可选功能)

参数优先级

  • 显示调用set设置值
  • 命令行参数
  • 环境变量
  • key/value存储
  • 默认值

包位置

github.com/spf13/viper

功能介绍

设置默认值

viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})

读取配置文件

Viper只支持单个配置文件,如果没有扩展名需要指定配置类型

viper.SetConfigName("config") // 配置文件名称(无扩展名)
viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则需要配置此项
viper.AddConfigPath("/etc/appname/")   // 查找配置文件所在的路径
viper.AddConfigPath("$HOME/.appname")  // 多次调用以添加多个搜索路径
viper.AddConfigPath(".")               // 还可以在工作目录中查找配置
err := viper.ReadInConfig() // 查找并读取配置文件
if err := viper.ReadInConfig(); err != nil {
    if _, ok := err.(viper.ConfigFileNotFoundError); ok {
        // 配置文件未找到错误;如果需要可以忽略
    } else {
        // 配置文件被找到,但产生了另外的错误
    }
}

写入配置文件

支持,但是用的比较少

监控并重新读取配置文件

支持在运行过程中获取配置文件的变更,检测配置变化了就调用回调函数

确保在调用WatchConfig()之前添加了所有的配置路径。

viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
  // 配置文件发生变更之后会调用的回调函数
    fmt.Println("Config file changed:", e.Name)
})

从内存读取配置

那我直接设置配置不好吗,或者常量,不知道为啥有这个功能

viper.SetConfigType("yaml") // 或者 viper.SetConfigType("YAML")

// 任何需要将此配置添加到程序中的方法。
var yamlExample = []byte(`
Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
  jacket: leather
  trousers: denim
age: 35
eyes : brown
beard: true
`)

viper.ReadConfig(bytes.NewBuffer(yamlExample))

viper.Get("name") // 这里会得到 "steve"

覆盖设置

viper.Set("Verbose", true)
viper.Set("LogFile", LogFile)

使用环境变量

示例

package main

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

)

func main() {

    viper.SetEnvPrefix("spf") // 前缀将自动转为大写
    viper.BindEnv("id")       
    os.Setenv("SPF_ID", "13") // 设置环境变量

    id := viper.Get("id") // 13
    fmt.Println(id)
}

使用Flag

package main

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

func main() {

    flag.String("flagname", "why", "help message for flagname")
    pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
    pflag.Parse()
    viper.BindPFlags(pflag.CommandLine)
    flagname := viper.Get("flagname") // 13
    fmt.Println(flagname)
}

数据获取方式

  • Get(key string) : interface{}
  • GetBool(key string) : bool
  • GetFloat64(key string) : float64
  • GetInt(key string) : int
  • GetIntSlice(key string) : []int
  • GetString(key string) : string
  • GetStringMap(key string) : map[string]in
  • GetStringMapString(key string) : map[str
  • GetStringSlice(key string) : []string
  • GetTime(key string) : time.Time
  • GetDuration(key string) : time.Duration
  • IsSet(key string) : bool
  • AllSettings() : map[string]interface{}

访问嵌套字段

示例json配置文件

{
    "host": {
        "address": "localhost",
        "port": 5799
    },
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        },
        "warehouse": {
            "host": "198.0.0.1",
            "port": 2112
        }
    }
}

可以通过分隔符路径获取

viper.GetString("datastore.metric.host")

如果直接有"datastore.metric.host"字段就会覆盖该结果

{
    "datastore.metric.host": "0.0.0.0",
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        }
}

子树

示例配置

app:
  cache1:
    max-items: 100
    item-size: 64
  cache2:
    max-items: 200
    item-size: 80

通过子树的方式进行不同配置的使用

func NewCache(cfg *Viper) *Cache {...}

cfg1 := viper.Sub("app.cache1")
cache1 := NewCache(cfg1)

cfg2 := viper.Sub("app.cache2")
cache2 := NewCache(cfg2)

反序列化

type config struct {
    Port int
    Name string
    PathMap string `mapstructure:"path_map"`
}

var C config

err := viper.Unmarshal(&C)
if err != nil {
    t.Fatalf("unable to decode into struct, %v", err)
}

如果key有.,需要重新制定分隔符

v := viper.NewWithOptions(viper.KeyDelimiter("::"))

v.SetDefault("chart::values", map[string]interface{}{
    "ingress": map[string]interface{}{
        "annotations": map[string]interface{}{
            "traefik.frontend.rule.type":                 "PathPrefix",
            "traefik.ingress.kubernetes.io/ssl-redirect": "true",
        },
    },
})

type config struct {
    Chart struct{
        Values map[string]interface{}
    }
}

var C config

v.Unmarshal(&C)

还支持嵌套

type config struct {
    Module struct {
        Enabled bool

        moduleConfig `mapstructure:",squash"`
    }
}

// moduleConfig could be in a module specific package
type moduleConfig struct {
    Token string
}

var C config

err := viper.Unmarshal(&C)
if err != nil {
    t.Fatalf("unable to decode into struct, %v", err)
}

示例

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "github.com/spf13/viper"
)

func main() {
    viper.SetConfigName("config")  // 指定配置文件名称(不需要带后缀)
    viper.SetConfigType("yaml")    // 指定配置文件类型
    viper.AddConfigPath("./conf/") // 指定查找配置文件的路径(这里使用相对路径)
    err := viper.ReadInConfig()    // 读取配置信息
    if err != nil {                // 读取配置信息失败
        panic(fmt.Errorf("Fatal error config file: %s \n", err))
    }
    r := gin.Default()
    if err := r.Run(fmt.Sprintf(":%d", viper.Get("port"))); err != nil {
        panic(err)
    }
}

配置文件为./conf/config.yaml

port: 4399