viper
Viper是什么?
Viper 是 Go 应用程序的完整配置解决方案,包括 12-Factor 应用程序。它旨在在应用程序中工作,并可以处理所有类型的配置需求和格式。它支持:
- 默认配置
- 从 JSON, TOML, YAML, HCL 和 Java 属性配置文件读取数据
- 实时查看和重新读取配置文件(可选)
- 从环境变量中读取
- 从远程配置系统(etcd 或 Consul)读取数据并监听变化
- 从命令行参数读取
- 从 buffer 中读取
- 设置显式值
Viper 可以被认为是所有应用程序配置需求的注册表。
Viper 使用以下优先级顺序。每个项目优先于其下方的项目:
- explicit call to Set
- flag
- env
- config
- key/value store
- default
- Viper配置键不区分大小写。
建立默认值
viper.SetDefault("ContentDir", "content") viper.SetDefault("LayoutDir", "layouts") viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
读取配置文件
Viper 需要极少的配置让它知道去哪里查找配置文件。Viper 支持 JSON, TOML, YAML, HCL 和 Java 属性配置文件。Viper 可以搜索多个路径,但目前单个 Viper 实例仅 支持单个配置文件。Viper 不默认任何配置搜索路径,将默认决策留给应用程序。
viper.SetConfigName("config") // 配置文件的名字,不包括拓展名 viper.AddConfigPath("/etc/appname/") // 添加配置文件的路径 viper.AddConfigPath("$HOME/.appname") // 多次调用以添加多个搜索路径 viper.AddConfigPath(".") // .代表当前项目的工作目录 err := viper.ReadInConfig() //查找读取配置文件 if err != nil { // Handle errors reading the config file panic(fmt.Errorf("Fatal error config file: %s \n", err)) }
监听和重新读取配置文件
Viper 支持在运行时让应用程序实时读取配置文件。
只需告诉 viper 实例 watchConfig 即可。你可以选择为 Viper 提供当每次发生更改时运行的函数。
确保在调用 WatchConfig() 之前添加所有的配置路径 ConfigPath
viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { fmt.Println("Config file changed:", e.Name) })
从 io.Reader 读取配置
Viper 预定义了许多配置源,例如文件,环境变量,标志和远程 K/V 存储,但你不受它们的约束。你还可以实现自己的需要的配置源并将其提供给 viper。
viper.SetConfigType("yaml") // or viper.SetConfigType("YAML") // any approach to require this configuration into your program. 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")
覆盖设置
这些可以来自命令行标志,也可以来自你自己的应用程序逻辑。
viper.Set("Verbose", true) viper.Set("LogFile", LogFile)
注册和使用别名
别名允许多个键引用单个值
viper.RegisterAlias("loud", "Verbose") viper.Set("verbose", true) viper.Set("loud", true) viper.GetBool("loud") // true viper.GetBool("verbose") // true
使用环境变量
Viper 完全支持环境变量。这使得 12 factor 应用程序可以开箱即用。 五种方法可以帮助使用 ENV:
AutomaticEnv() BindEnv(string...) : error SetEnvPrefix(string) SetEnvKeyReplacer(string...) *strings.Replacer AllowEmptyEnvVar(bool)
在处理环境变量时,重要的是要认识到 Viper 将环境变量视为区分大小写的变量。
Viper 提供了一种机制来确保 ENV 变量是唯一的。通过使用 SetEnvPrefix,可以告诉 Viper 在读取环境变量时使用前缀。BindEnv 和 AutomaticEnv 都将使用此前缀。
BindEnv 需要一个或两个参数。第一个参数是键名,第二个是环境变量的名称。环境变量的名称区分大小写。如果未提供 ENV 变量名,则 Viper 将自动假设键名与 ENV 变量名称匹配, 但 ENV 变量为 IN ALL CAPS。 当明确提供ENV变量名称时,它不会自动添加前缀。
使用 ENV 变量时要认识到的一件重要事情是每次访问时都会读取该值。当调用 BindEnv 时,Viper不会修复该值。
AutomaticEnv 是一个强大的帮手,特别是与 SetEnvPrefix 结合使用时。每当发出 viper.Get 请求时,Viper 都会检查环境变量。它将适用以下规则。 它将检查一个环境变量,其名称与大写的键匹配,如果设置了 EnvPrefix,则以它为前缀。
SetEnvKeyReplacer 允许你使用 strings.Replacer 对象来重写 Env 键。如果你想在 Get() 调用中使用 - 或者某些东西,但希望你的环境变量使用 _ 分隔符, 这是很有用的。使用它的一个例子可以在 viper_test.go 中找到。
默认情况下,空环境变量被视为未设置,并将回退到下一个配置源。要设置空环境变量,请使用 AllowEmptyEnv 方法。
SetEnvPrefix("spf") // 将自动大写 BindEnv("id") os.Setenv("SPF_ID", "13") id := Get("id") // 13
用Flags
Viper 具有绑定到标志的能力。具体来说,Viper支持Cobra库中使用的Pflag。
与BindEnv类似,该值不是在调用绑定方法时设置的,而是在访问该方法时设置的。这意味着你可以根据需要尽早进行绑定,即使在init()函数中也是如此。
对于单个标志,BindPFlag()方法提供此功能。
serverCmd.Flags().Int("port", 1138, "Port to run Application server on") viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
你还可以绑定一组现有的pflags (pflag.FlagSet):
举个例子:
pflag.Int("flagname", 1234, "help message for flagname") pflag.Parse() viper.BindPFlags(pflag.CommandLine) i := viper.GetInt("flagname") // 从viper而不是从pflag检索值
在 Viper 中使用 pflag 并不阻碍其他包中使用标准库中的 flag 包。pflag 包可以通过导入这些 flags 来处理flag包定义的flags。这是通过调用pflag包提供的便利函数AddGoFlagSet()来实现的。
例如:
package main import ( "flag" "github.com/spf13/pflag" ) func main() { // 使用标准库 "flag" 包 flag.Int("flagname", 1234, "help message for flagname") pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.Parse() viper.BindPFlags(pflag.CommandLine) i := viper.GetInt("flagname") // 从 viper 检索值 ... }
从Viper获取值
在Viper中,有几种方法可以根据值的类型获取值。存在以下功能和方法:
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]interface{} GetStringMapString(key string) : map[string]string GetStringSlice(key string) : []string GetTime(key string) : time.Time GetDuration(key string) : time.Duration IsSet(key string) : bool AllSettings() : map[string]interface{}
需要认识到的一件重要事情是,每一个Get方法在找不到值的时候都会返回零值。为了检查给定的键是否存在,提供了IsSet()方法。
例如:
viper.GetString("logfile") // 不区分大小写的设置和获取 if viper.GetBool("verbose") { fmt.Println("verbose enabled") }
访问嵌套的键
访问器方法也接受深度嵌套键的格式化路径。例如,如果加载下面的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") // (返回 "127.0.0.1")
如果存在与分隔的键路径匹配的键,则返回其值。例如:
{ "datastore.metric.host": "0.0.0.0", "host": { "address": "localhost", "port": 5799 }, "datastore": { "metric": { "host": "127.0.0.1", "port": 3099 }, "warehouse": { "host": "198.0.0.1", "port": 2112 } } }
GetString(“datastore.metric.host”) // 返回 “0.0.0.0”
go语言中的配置管理神器 --viper 一(二)https://developer.aliyun.com/article/1391730