https://blog.csdn.net/cs380637384/article/details/81217767

https://blog.csdn.net/songhao8080/article/details/103669858?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

什么是Viper?

Viper是Go应用程序的完整配置解决方案,包括12-Factor应用程序。它旨在在应用程序中工作,并可以处理所有类型的配置需求和格式。它支持:

  • 设置默认值
  • 从JSON,TOML,YAML,HCL和Java属性配置文件中读取
  • 实时观看和重新读取配置文件(可选)
  • 从环境变量中读取
  • 从远程配置系统(etcd或Consul)读取,并观察变化
  • 从命令行标志读取
  • 从缓冲区读取
  • 设置显式值

Viper可以被认为是所有应用程序配置需求的注册表。

为何选择Viper?

构建现代应用程序时,您不必担心配置文件格式; 你想专注于构建真棒软件。Viper就是为此提供帮助的。

Viper为您做了以下事情:

  1. 以JSON,TOML,YAML,HCL或Java属性格式查找,加载和解组配置文件。
  2. 提供一种机制来为不同的配置选项设置默认值。
  3. 提供一种机制来为通过命令行标志指定的选项设置覆盖值。
  4. 提供别名系统,轻松重命名参数,而不会破坏现有代码。
  5. 可以很容易地区分用户提供命令行或配置文件与默认值相同的时间。

Viper使用以下优先顺序。每个项目优先于其下方的项目:

 

使用Viper设置默认值

  • explicit call to Set
  • flag
  • env
  • config
  • key/value store
  • default
  1. viper.SetDefault(“ContentDir”, “content”)
  2. viper.SetDefault(“LayoutDir”, “layouts”)
  3. viper.SetDefault(“Taxonomies”, map[string]string{“tag”: “tags”, “category”: “categories”})

Viper读取配置文件

Viper需要最少的配置,因此它知道在哪里查找配置文件。Viper支持JSON,TOML,YAML,HCL和Java Properties文件。Viper可以搜索多个路径,但目前单个Viper实例仅支持单个配置文件。Viper不会默认使用任何配置搜索路径,而是将默认值决定应用于应用程序。

以下是如何使用Viper搜索和读取配置文件的示例。不需要任何特定路径,但应在预期配置文件的位置提供至少一个路径。

 

  1. viper.SetConfigName(“config”) // name of config file (without extension)
  2. viper.AddConfigPath(“/etc/appname/”) // path to look for the config file in
  3. viper.AddConfigPath(“$HOME/.appname”) // call multiple times to add many search paths
  4. viper.AddConfigPath(“.”) // optionally look for config in the working directory
  5. err := viper.ReadInConfig() // Find and read the config file
  6. if err != nil { // Handle errors reading the config file
  7. panic(fmt.Errorf(“Fatal error config file: %s \n”, err))
  8. }

监听并重新读取配置文件

Viper支持在运行时让应用程序实时读取配置文件。

需要重新启动服务器以使配置生效的日子已经一去不复返了,viper驱动的应用程序可以在运行时读取配置文件的更新,而不会错过任何一个节拍。

只需告诉viper实例watchConfig即可。您可以选择为Viper提供每次发生更改时运行的功能。

确保在调用之前添加所有configPath WatchConfig()

  1. viper.WatchConfig()
  2. viper.OnConfigChange(func(e fsnotify.Event) {
  3. fmt.Println(“Config file changed:”, e.Name)
  4. })

从io.Reader读取配置

Viper预定义了许多配置源,例如文件,环境变量,标志和远程K / V存储,但您不受它们的约束。您还可以实现自己的必需配置源并将其提供给viper。

  1. viper.SetConfigType(“yaml”) // 或viper.SetConfigType(“YAML”)
  2. //任何需要将此配置放入程序的方法
  3. var yamlExample = []byte(`
  4. Hacker: true
  5. name: steve
  6. hobbies:
  7. – skateboarding
  8. – snowboarding
  9. – go
  10. clothing:
  11. jacket: leather
  12. trousers: denim
  13. age: 35
  14. eyes : brown
  15. beard: true
  16. `)
  17. viper.ReadConfig(bytes.NewBuffer(yamlExample))
  18. viper.Get(“name”)

Viper设置并覆盖配置值

  1. viper.Set(“Verbose”, true)
  2. viper.Set(“LogFile”, LogFile)

Viper注册和使用别名

别名允许多个键引用单个值

  1. viper.RegisterAlias(“loud”, “Verbose”)
  2. viper.Set(“verbose”, true) // same result as next line
  3. viper.Set(“loud”, true) // same result as prior line
  4. viper.GetBool(“loud”) // true
  5. viper.GetBool(“verbose”) // true

Viper使用环境变量

Viper完全支持环境变量。有四种方法可以帮助使用ENV:

  1. AutomaticEnv()
  2. BindEnv(string…) : error
  3. SetEnvPrefix(string)
  4. SetEnvKeyReplacer(string…) *strings.Replacer

BindEnv需要一个或两个参数。第一个参数是键名,第二个是环境变量的名称。环境变量的名称区分大小写。如果未提供ENV变量名,则Viper将自动假设密钥名称与ENV变量名称匹配,但ENV变量为IN ALL CAPS。当您明确提供ENV变量名称时,它不会自动添加前缀。

使用ENV变量时要认识到的一件重要事情是每次访问时都会读取该值。Viper在BindEnv调用时不会修复该值。

AutomaticEnv尤其是当与结合了强大的帮手 SetEnvPrefix。调用时,Viper将在任何viper.Get请求发出时检查环境变量。它将适用以下规则。它将检查一个环境变量,其名称与大写的键匹配,并以EnvPrefix前缀。

SetEnvKeyReplacer允许您使用strings.Replacer对象重写Env键到一定程度。如果要-Get()调用中使用或使用某些内容 ,但希望环境变量使用_分隔符,则此选项非常有用。可以在中找到使用它的示例viper_test.go

ENV实例

  1. SetEnvPrefix(“spf”) //将自动大写
  2. BindEnv(“id”)
  3. os.Setenv(“SPF_ID”, “13”) // 通常在应用以外完成
  4. id := Get(“id”) // 13

使用flag

Viper能够绑定到flag。

就像BindEnv,在调用绑定方法时,不会设置该值。这意味着您可以尽早绑定,甚至可以在init()函数中绑定 。

对于单个标志,该BindPFlag()方法提供此功能。

  1. serverCmd.Flags().Int(“port”, 1138, “Port to run Application server on”)
  2. viper.BindPFlag(“port”, serverCmd.Flags().Lookup(“port”))

您还可以绑定一组现有的pflags(pflag.FlagSet)

  1. pflag.Int(“flagname”, 1234, “help message for flagname”)
  2. pflag.Parse()
  3. viper.BindPFlags(pflag.CommandLine)
  4. i := viper.GetInt(“flagname”)

 

在Viper中使用pflag并不排除使用 标准库中使用标志包的其他包。pflag包可以通过导入这些标志来处理为标志包定义的标志。这是通过调用名为AddGoFlagSet()的pflag包提供的便利函数来实现的。

  1. package main
  2. import (
  3. “flag”
  4. “github.com/spf13/pflag”
  5. )
  6. func main() {
  7. // using standard library “flag” package
  8. flag.Int(“flagname”, 1234, “help message for flagname”)
  9. pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
  10. pflag.Parse()
  11. viper.BindPFlags(pflag.CommandLine)
  12. i := viper.GetInt(“flagname”) // retrieve value from viper
  13. }

flag接口

如果您不使用,Viper提供两个Go接口来绑定其他标志系统Pflags

FlagValue代表一个标志。这是一个关于如何实现此接口的非常简单的示例:

  1. type myFlag struct {}
  2. funcf myFlagHasChanged()bool { return false }
  3. funcf myFlagName()string { return “ my-flag-name ” }
  4. funcf myFlagValueString()string { return “ my -flag-value “ }
  5. funcf myFlagValueType()string { returnstring ” }

一旦你的flag实现了这个接口,你可以告诉Viper绑定它:

viper.BindFlagValue("my-flag-name", myFlag{})

远程key/value存储

要在Viper中启用远程支持,请对viper/remote 包进行空白导入:

import _ "github.com/spf13/viper/remote"

Viper将读取key/value存储(如etcd或Consul)中的路径检索的配置字符串(如JSON,TOML,YAML或HCL)。这些值优先于默认值,但会被从磁盘,标志或环境变量检索的配置值覆盖。

Viper使用crypt从K / V存储中检索配置,这意味着您可以存储加密的配置值,并在拥有正确的gpg密钥环时自动解密。加密是可选的。

您可以将远程配置与本地配置结合使用,也可以独立使用。

crypt有一个命令行帮助程序,您可以使用它来将配置放入K / V存储区。crypthttp://127.0.0.1:4001上默认为etcd 。

  1. $ go get github.com/xordataexchange/crypt/bin/crypt
  2. $ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json

确认您的值已设置:

$ crypt get -plaintext /config/hugo.json

远程key/value存储示例 – 未加密

  1. viper.AddRemoteProvider(“etcd”, “http://127.0.0.1:4001”,“/config/hugo.json”)
  2. viper.SetConfigType(“json”) //因为字节流中没有文件扩展名,支持的扩展名是“json”,“toml”,“yaml”,“yml”,“properties”,“props”,“prop”
  3. err := viper.ReadRemoteConfig()

远程key/value存储示例 – 加密

  1. viper.AddSecureRemoteProvider(“etcd”,“http://127.0.0.1:4001”,“/config/hugo.json”,“/etc/secrets/mykeyring.gpg”)
  2. viper.SetConfigType(“json”) //因为字节流中没有文件扩展名,支持的扩展名是“json”,“toml”,“yaml”,“yml”,“properties”,“props”,“prop”
  3. err := viper.ReadRemoteConfig()

监听etcd中的变化 – 未加密

  1. //或者,您可以创建一个新的viper实例
  2. var runtime_viper = viper.New()
  3. runtime_viper.AddRemoteProvider(“etcd”, “http://127.0.0.1:4001”, “/config/hugo.yml”)
  4. runtime_viper.SetConfigType(“yaml”)
  5. // 第一次从远程配置中读取
  6. err := runtime_viper.ReadRemoteConfig()
  7. //解密配置
  8. runtime_viper.Unmarshal(&runtime_conf)
  9. // 打开一个goroutine来永远监听远程变化
  10. go func(){
  11. for {
  12. time.Sleep(time.Second * 5) // 每次请求后延迟
  13. err := runtime_viper.WatchRemoteConfig()
  14. if err != nil {
  15. log.Errorf(“unable to read remote config: %v”, err)
  16. continue
  17. }
  18. //将新配置解组到我们的运行时配置结构中。您还可以使用通道
  19. //实现信号以通知系统更改
  20. runtime_viper.Unmarshal(&runtime_conf)
  21. }
  22. }()

Viper获取值

在Viper中,有几种方法可以根据值的类型获取值。存在以下功能和方法:

  1. Get(key string) : interface{}
  2. GetBool(key string) : bool
  3. GetFloat64(key string) : float64
  4. GetInt(key string) : int
  5. GetString(key string) : string
  6. GetStringMap(key string) : map[string]interface{}
  7. GetStringMapString(key string) : map[string]string
  8. GetStringSlice(key string) : []string
  9. GetTime(key string) : time.Time
  10. GetDuration(key string) : time.Duration
  11. IsSet(key string) : bool
  12. AllSettings() : map[string]interface{}

如果找不到,每个Get函数都将返回零值。IsSet()方法检查给定密钥是否存在。

实例:

  1. viper.GetString(“logfile”) // case-insensitive Setting & Getting
  2. if viper.GetBool(“verbose”) {
  3. fmt.Println(“verbose enabled”)
  4. }

访问嵌套

访问器方法也接受深层嵌套键的格式化路径。例如,如果加载了以下JSON文件:

  1. {
  2. “host”: {
  3. “address”: “localhost”,
  4. “port”: 5799
  5. },
  6. “datastore”: {
  7. “metric”: {
  8. “host”: “127.0.0.1”,
  9. “port”: 3099
  10. },
  11. “warehouse”: {
  12. “host”: “198.0.0.1”,
  13. “port”: 2112
  14. }
  15. }
  16. }

Viper可以通过传递.分隔的键路径来访问嵌套字段:

GetString("datastore.metric.host") // (returns "127.0.0.1")

这符合上面建立的优先规则; 搜索路径将在剩余的配置注册表中级联,直到找到。

例如,给定此配置文件,都datastore.metric.host和 datastore.metric.port已经定义(并且可以被覆盖)。如果另外datastore.metric.protocol在默认值中定义,Viper也会找到它。

但是,如果使用立即值datastore.metric覆盖(通过标志,环境变量,Set()方法,…),则所有子键 datastore.metric变为未定义,它们将被更高优先级的配置级别“遮蔽”。

最后,如果存在与分隔的键路径匹配的键,则将返回其值。例如

  1. {
  2. “datastore.metric.host”: “0.0.0.0”,
  3. “host”: {
  4. “address”: “localhost”,
  5. “port”: 5799
  6. },
  7. “datastore”: {
  8. “metric”: {
  9. “host”: “127.0.0.1”,
  10. “port”: 3099
  11. },
  12. “warehouse”: {
  13. “host”: “198.0.0.1”,
  14. “port”: 2112
  15. }
  16. }
  17. }
  18. GetString(“datastore.metric.host”) // returns “0.0.0.0”

提取sub-tree

例如

  1. app:
  2. cache1:
  3. max-items: 100
  4. item-size: 64
  5. cache2:
  6. max-items: 200
  7. item-size: 80
  8. #执行后
  9. subv := viper.Sub(“app.cache1”)
  10. #subv为
  11. max-items:100
  12. item-size:64

假设

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

它根据格式化为的配置信息创建缓存subv。现在可以轻松地分别创建这两个缓存:

  1. cfg1 := viper.Sub(“app.cache1”)
  2. cache1 := NewCache(cfg1)
  3. cfg2 := viper.Sub(“app.cache2”)
  4. cache2 := NewCache(cfg2)

遍历

您还可以选择Unmarshaling all或特定值到struct,map等。

有两种方法可以做到这一点:

  1. Unmarshal(rawVal interface{}) : error
  2. UnmarshalKey(key string, rawVal interface{}) : error

例如:

  1. type config struct {
  2. Port int
  3. Name string
  4. PathMap string `mapstructure:”path_map”`
  5. }
  6. var C config
  7. err := Unmarshal(&C)
  8. if err != nil {
  9. t.Fatalf(“unable to decode into struct, %v”, err)
  10. }

转为字符串

您可能需要将viper中保存的所有设置变为字符串,而不是将它们写入文件。您可以使用您喜欢的格式的marshaller和返回的配置AllSettings()

  1. import (
  2. yaml “gopkg.in/yaml.v2”
  3. // …
  4. )
  5. func yamlStringSettings() string {
  6. c := viper.AllSettings()
  7. bs, err := yaml.Marshal(c)
  8. if err != nil {
  9. t.Fatalf(“unable to marshal config to YAML: %v”, err)
  10. }
  11. return string(bs)
  12. }

Viper or Vipers?

Viper随时可以使用。开始使用Viper无需配置或初始化。由于大多数应用程序都希望使用单个中央存储库进行配置,因此viper软件包提供了此功能。它类似于单身人士。

在上面的所有示例中,他们演示了使用viper的单例式方法。

使用多个Viper

您还可以创建许多不同的viper,以便在您的应用程序中使用。每个都有自己独特的配置和价值观。每个都可以从不同的配置文件,键值存储等中读取.viper包支持的所有功能都被镜像为viper上的方法。

例如

  1. x := viper.New()
  2. y := viper.New()
  3. x.SetDefault(“ContentDir”, “content”)
  4. y.SetDefault(“ContentDir”, “foobar”)

使用多viper时,用户可以跟踪不同的viper

发表评论

邮箱地址不会被公开。 必填项已用*标注