viper - go 配置管理

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站

Viper 是适用于 Go 应用程序的完整配置解决方案。

安装

go get github.com/spf13/viper

什么是 Viper?

Viper 是适用于 Go 应用程序的完整配置解决方案。它被设计用于在应用程序中工作,并且可以处理所有类型的配置需求和格式。它支持以下特性:

  • 设置默认值
  • 从 JSON、TOML、YAML、HCL、envfile 和 Java properties 格式的配置文件读取配置信息
  • 实时监控和重新读取配置文件(可选)
  • 从环境变量中读取
  • 从远程配置系统(etcd 或 Consul)读取并监控配置变化
  • 从命令行参数读取配置
  • 从 buffer 读取配置
  • 显式配置值

Viper 配置优先级

Viper 会按照下面的优先级。每个项目的优先级都高于它下面的项目:

  • 显式调用 Set 设置值
  • 命令行参数(flag)
  • 环境变量
  • 配置文件
  • key/value 存储
  • 默认值

重要:目前 Viper 配置的键(Key)是大小写不敏感的。

把值存入 Viper

设置默认值

viper.SetDefault("ContentDir", "content")

读取配置文件

Viper 可以搜索多个路径,但目前单个 Viper 实例只支持单个配置文件。Viper 不默认任何配置搜索路径,将默认决策留给应用程序。

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

viper.SetConfigFile("./config.yaml") // 指定配置文件路径
viper.SetConfigName("config") // 配置文件名称(无扩展名)
viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则需要配置此项
viper.AddConfigPath("/etc/appname/")   // 查找配置文件所在的路径
viper.AddConfigPath("$HOME/.appname")  // 多次调用以添加多个搜索路径
viper.AddConfigPath(".")               // 还可以在工作目录中查找配置
err := viper.ReadInConfig() // 查找并读取配置文件
if err != nil { // 处理读取配置文件的错误
	panic(fmt.Errorf("Fatal error config file: %s \n", err))
}

在加载配置文件出错时,你可以像下面这样处理找不到配置文件的特定情况:

if err := viper.ReadInConfig(); err != nil {
    if _, ok := err.(viper.ConfigFileNotFoundError); ok {
        // 配置文件未找到错误;如果需要可以忽略
    } else {
        // 配置文件被找到,但产生了另外的错误
    }
}

// 配置文件找到并成功解析

写入配置文件

从配置文件中读取配置是有用的,但是有时候你想要在运行时所做的所有修改。为此,可以使用下面一组命令,每个命令都有自己的用途:

  • WriteConfig - 将当前 的 viper 配置写入预定义的路径并覆盖(如果存在的话)。如果没有预定义的路径,则报错。
  • SafeWriteConfig - 跟 WriteConfig 一样,不过如果文件已经存在则不覆盖。
  • WriteConfigAs - 将当前的 viper 配置写入给定的文件路径。将覆盖给定的文件(如果它存在的话)
  • SafeWriteCofnigAs - 将当前的 viper 配置写入给定的文件路径。不会覆盖已有的文件。
viper.WriteConfig() // 将当前配置写入“viper.AddConfigPath()”和“viper.SetConfigName”设置的预定义路径
viper.SafeWriteConfig()
viper.WriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.config") // 因为该配置文件写入过,所以会报错
viper.SafeWriteConfigAs("/path/to/my/.other_config")

监控并重新读取配置文件

Viper 支持在运行时实时读取配置文件的功能。

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

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

从 io.Reader 读取配置

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)

注册和使用别名

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

viper.RegisterAlias("loud", "Verbose")  // 注册别名(此处loud和Verbose建立了别名)

viper.Set("verbose", true) // 结果与下一行相同
viper.Set("loud", true)   // 结果与前一行相同

viper.GetBool("loud") // true
viper.GetBool("verbose") // true

使用环境变量

Viper 完全支持环境变量。有五种方法可以使用 ENV 相关特性:

  • AutomaticEnv()
  • BuildEnv(string...): error
  • SetEnvPrefix(string)
  • SetEnvKeyReplacer(string...) *strings.Replacer
  • AllowEmptyEnv(bool)

使用 ENV 变量时,务必意识到 Viper 将 ENV 变量视为区分大小写。

Viper 提供了一种机制来确保 ENV 变量是唯一的。通过使用 SetEnvPrefix,你可以告诉 Viper 在读取环境变量时使用前缀。BindEnvAutomaticEnv 都将使用这个前缀。

SetEnvPrefix("spf") // 将自动转为大写
BindEnv("id")

os.Setenv("SPF_ID", "13") // 通常是在应用程序之外完成的

id := Get("id") // 13

使用 Flags

Viper 具有绑定到标志的能力。

BindEnv 类似,该值不是在调用绑定方法时设置的,而是在访问该方法时设置的。这意味着你可以根据需要尽早进行绑定,即使在 init() 函数中也是如此。

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

serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))

从 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() 方法。

访问嵌套的键

访问器方法也接受深度嵌套键的格式化路径。例如,如果加载下面的 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("database.metric.host") // 127.0.0.1

提取子树

viper.Sub("app.cache")

反序列化

你可以选择将所有或特定的值解析道结构体、map 等。

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

  • Unmarshal(rawVal interface{}) : error
  • UnmarshalKey(key string, rawVal interface{}) : error

示例:

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)
}

序列化成字符串

你可能需要将 viper 中保存的所有设置序列化到一个字符串中,而不是将它们写入到一个文件中。你可以将自己喜欢的格式的序列化器与 AllSettings() 返回的配置一起使用。

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白如意i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值