比拟有名的计划有

应用viper治理配置


  • 反对多种配置文件格式,包含 JSON,TOML,YAML,HECL,envfile,甚至还包含Java properties
  • 反对为配置项设置默认值
  • 能够通过命令行参数笼罩指定的配置项
  • 反对参数别名


viper依照这个优先级(从高到低)获取配置项的取值:

  • explicit call to Set: 在代码逻辑中通过viper.Set()间接设置配置项的值
  • flag:命令行参数
  • env:环境变量
  • config:配置文件
  • key/value store:etcd或者consul
  • default:默认值


依照这个优先级(从高到低)获取配置项的取值:

  • explicit call to Set: 在代码逻辑中通过viper.Set()间接设置配置项的值
  • flag:命令行参数
  • env:环境变量
  • config:配置文件
  • key/value store:etcd或者consul
  • default:默认值


优先级


验证一下 viper.Set() 的优先级高于 配置文件


package mainimport (    "fmt"    "github.com/spf13/viper")func main() {    loadConfig()}func loadConfig() {    configVar := "shuang-config.yaml"    configVar = "" // 这行如果正文掉,则从指定的configVar读取配置文件;否则就各种条件去找了    viper.Set("Global.Source", "优先级最高")    if configVar != "" {        // SetConfigFile 显式定义配置文件的门路、名称和扩展名。        // Viper 将应用它而不查看任何配置门路。        viper.SetConfigFile(configVar)    } else {        // 如果没有显式指定配置文件,则        // 会去上面的门路里找文件名`cui-config`的文件  name of config file (without extension)        // 依照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的程序(竟然还反对Java用的properties)        viper.SetConfigName("cui-config")        viper.AddConfigPath("/etc/myapp") // 找寻的门路        viper.AddConfigPath("$HOME/.myapp/")        viper.AddConfigPath(".")    }    err := viper.ReadInConfig()    if err != nil {        panic(fmt.Errorf("error reading config: %s", err))    }    fmt.Printf("到底用的是哪个配置文件: '%s'\n", viper.ConfigFileUsed())    fmt.Printf("Global.Source这个字段的值为: '%s'\n", viper.GetString("global.source"))}


输入:

到底用的是哪个配置文件: '/Users/fliter/config-demo/cui-config.yaml'Global.Source这个字段的值为: '优先级最高'





验证一下 环境变量 的优先级高于 配置文件


package mainimport (    "fmt"    "github.com/spf13/viper")func main() {    loadConfig()}func loadConfig() {    configVar := "shuang-config.yaml"    configVar = "" // 这行如果正文掉,则从指定的configVar读取配置文件;否则就各种条件去找了    viper.Set("Global.Source", "优先级最高")    viper.AutomaticEnv()    if configVar != "" {        // SetConfigFile 显式定义配置文件的门路、名称和扩展名。        // Viper 将应用它而不查看任何配置门路。        viper.SetConfigFile(configVar)    } else {        // 如果没有显式指定配置文件,则        // 会去上面的门路里找文件名`cui-config`的文件  name of config file (without extension)        // 依照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的程序(竟然还反对Java用的properties)        viper.SetConfigName("cui-config")        viper.AddConfigPath("/etc/myapp") // 找寻的门路        viper.AddConfigPath("$HOME/.myapp/")        viper.AddConfigPath(".")    }    err := viper.ReadInConfig()    if err != nil {        panic(fmt.Errorf("error reading config: %s", err))    }    fmt.Printf("到底用的是哪个配置文件: '%s'\n", viper.ConfigFileUsed())    fmt.Printf("LANG这个字段的值为: '%s'\n", viper.GetString("LANG"))}

viper.AutomaticEnv()会绑定所有环境变量,

如果只心愿绑定特定的,能够应用SetEnvPrefix("global.source", "MYAPP_GLOAL_SOURCE"),留神这个函数不会主动加上MYAPP的前缀.




验证一下 命令行参数的优先级高于 配置文件


viper能够配合pflag来应用,pflag能够了解为规范库flag的一个增强版,viper能够绑定到pflag上

和cobra,viper一样,pflag也是同一作者的作品




验证一下 默认值的优先级低于 配置文件


package mainimport (    "fmt"    "github.com/spf13/viper")func main() {    loadConfig()}func loadConfig() {    configVar := "shuang-config.yaml"    configVar = "" // 这行如果正文掉,则从指定的configVar读取配置文件;否则就各种条件去找了    //viper.Set("Global.Source", "优先级最高")    viper.AutomaticEnv()    viper.SetDefault("Global.Source", "优先级最低")    if configVar != "" {        // SetConfigFile 显式定义配置文件的门路、名称和扩展名。        // Viper 将应用它而不查看任何配置门路。        viper.SetConfigFile(configVar)    } else {        // 如果没有显式指定配置文件,则        // 会去上面的门路里找文件名`cui-config`的文件  name of config file (without extension)        // 依照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的程序(竟然还反对Java用的properties)        viper.SetConfigName("cui-config")        viper.AddConfigPath("/etc/myapp") // 找寻的门路        viper.AddConfigPath("$HOME/.myapp/")        viper.AddConfigPath(".")    }    err := viper.ReadInConfig()    if err != nil {        panic(fmt.Errorf("error reading config: %s", err))    }    fmt.Printf("到底用的是哪个配置文件: '%s'\n", viper.ConfigFileUsed())    fmt.Printf("Global.Source这个字段的值为: '%s'\n", viper.GetString("Global.Source"))}




Watch机制(配置更新后 热加载)


该机制能够监听配置文件的批改, 这样就实现了热加载,批改配置后,无需重启服务

  • 对于本地文件,是通过fsnotify实现的,而后通过一个回调函数去告诉利用来reload;
  • 对于Remote KV Store,目前只反对etcd,做法比拟ugly,(5秒钟)轮询一次 而不是watch api
package mainimport (    "fmt"    "time"    "github.com/fsnotify/fsnotify"    "github.com/spf13/viper")func main() {    loadConfig()}func loadConfig() {    configVar := "shuang-config.yaml"    configVar = "" // 这行如果正文掉,则从指定的configVar读取配置文件;否则就各种条件去找了    if configVar != "" {        // SetConfigFile 显式定义配置文件的门路、名称和扩展名。        // Viper 将应用它而不查看任何配置门路。        viper.SetConfigFile(configVar)    } else {        // 如果没有显式指定配置文件,则        // 会去上面的门路里找文件名`cui-config`的文件  name of config file (without extension)        // 依照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的程序(竟然还反对Java用的properties)        viper.SetConfigName("cui-config")        viper.AddConfigPath("/etc/myapp") // 找寻的门路        viper.AddConfigPath("$HOME/.myapp/")        viper.AddConfigPath(".")    }    viper.WatchConfig()    viper.OnConfigChange(func(e fsnotify.Event) {        fmt.Printf("配置文件 %s 产生了更改!!! 最新的Global.Source这个字段的值为 %s:", e.Name, viper.GetString("Global.Source"))    })    err := viper.ReadInConfig()    if err != nil {        panic(fmt.Errorf("error reading config: %s", err))    }    fmt.Printf("到底用的是哪个配置文件: '%s'\n", viper.ConfigFileUsed())    fmt.Printf("Global.Source这个字段的值为: '%s'\n", viper.GetString("Global.Source"))    time.Sleep(10000e9)}

Go viper 配置文件读取工具

动静获取配置文件(viper)




configor


Configor: 一个Golang配置工具,反对YAML,JSON,TOML,Shell环境,反对热加载

出自jinzhu大佬


package mainimport (    "fmt"    "github.com/jinzhu/configor")type Config struct {    APPName string `default:"app name"`    DB      struct {        Name     string        User     string `default:"root"`        Password string `required:"true" env:"DBPassword"`        Port     uint   `default:"3306"`    }    Contacts []struct {        Name  string        Email string `required:"true"`    }}func main() {    var conf = Config{}    err := configor.Load(&conf, "config.yml")    // err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml")  // 测试模式,也能够通过环境变量开启测试模式(CONFIGOR_DEBUG_MODE=true go run main.go ),这样就无需批改代码    //err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也能够通过环境变量开启具体模式(CONFIGOR_VERBOSE_MODE=true go run main.go ),这样就无需批改代码    if err != nil {        panic(err)    }    fmt.Printf("%v \n", conf)}


开启 测试模式 or 具体模式


既能够在代码中显式开启,如 err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml")

也能够通过环境变量开启,如 CONFIGOR_DEBUG_MODE=true go run main.go


加载多个配置文件


// application.yml 的优先级 大于 database.json, 排在后面的配置文件优先级大于排在后的的配置configor.Load(&Config, "application.yml", "database.json")


依据环境变量加载配置文件 or 从shell加载配置项


具体可参考 Golang Configor 配置文件工具


热更新


package mainimport (    "fmt"    "time"    "github.com/jinzhu/configor")type Config struct {    APPName string `default:"app name"`    DB      struct {        Name     string        User     string `default:"root"`        Password string `required:"true" env:"DBPassword"`        Port     uint   `default:"3306"`    }    Contacts []struct {        Name  string        Email string `required:"true"`    }}func main() {    var conf = Config{}    // reload模式,可实现热加载    err := configor.New(&configor.Config{        AutoReload:         true,        AutoReloadInterval: time.Second,        AutoReloadCallback: func(config interface{}) {            // config发生变化后登程什么操作            fmt.Printf("配置文件产生了变更%#v\n", config)        },    }).Load(&conf, "config.yml")    // 无reload模式    //err := configor.Load(&conf, "config.yml")    // err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml")  // 测试模式,也能够通过环境变量开启测试模式(CONFIGOR_DEBUG_MODE=true go run main.go ),这样就无需批改代码    //err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也能够通过环境变量开启具体模式(CONFIGOR_VERBOSE_MODE=true go run main.go ),这样就无需批改代码    if err != nil {        panic(err)    }    fmt.Printf("%v \n", conf)    time.Sleep(100000e9)}


残缺代码

本文由mdnice多平台公布