Viper 是适用于 Go 应用程序的完整配置解决方案。
安装
1 | 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
设置默认值
1 | viper.SetDefault("ContentDir", "content") |
读取配置文件
Viper 可以搜索多个路径,但目前单个 Viper 实例只支持单个配置文件。Viper 不默认任何配置搜索路径,将默认决策留给应用程序。
下面是一个如何使用 Viper 搜索和读取配置文件的示例。不需要任何特定的路径,但是至少应该提供一个配置文件预期出现的路径。
1 | viper.SetConfigFile("./config.yaml") // 指定配置文件路径 |
在加载配置文件出错时,你可以像下面这样处理找不到配置文件的特定情况:
1 | if err := viper.ReadInConfig(); err != nil { |
写入配置文件
从配置文件中读取配置是有用的,但是有时候你想要在运行时所做的所有修改。为此,可以使用下面一组命令,每个命令都有自己的用途:
WriteConfig
- 将当前 的 viper 配置写入预定义的路径并覆盖(如果存在的话)。如果没有预定义的路径,则报错。SafeWriteConfig
- 跟WriteConfig
一样,不过如果文件已经存在则不覆盖。WriteConfigAs
- 将当前的 viper 配置写入给定的文件路径。将覆盖给定的文件(如果它存在的话)SafeWriteCofnigAs
- 将当前的 viper 配置写入给定的文件路径。不会覆盖已有的文件。
1 | viper.WriteConfig() // 将当前配置写入“viper.AddConfigPath()”和“viper.SetConfigName”设置的预定义路径 |
监控并重新读取配置文件
Viper 支持在运行时实时读取配置文件的功能。
确保在调用
WatchConfig()
之前添加了所有的配置路径。
1 | viper.WatchConfig() |
从 io.Reader 读取配置
1 | viper.SetConfigType("yaml") // 或者 viper.SetConfigType("YAML") |
覆盖配置
这些可能来自命令行的配置,也可能来自你自己的应用程序逻辑。
1 | viper.Set("Verbose", true) |
注册和使用别名
别名允许多个键引用单个值:
1 | viper.RegisterAlias("loud", "Verbose") // 注册别名(此处loud和Verbose建立了别名) |
使用环境变量
Viper 完全支持环境变量。有五种方法可以使用 ENV 相关特性:
AutomaticEnv()
BuildEnv(string...): error
SetEnvPrefix(string)
SetEnvKeyReplacer(string...) *strings.Replacer
AllowEmptyEnv(bool)
使用 ENV 变量时,务必意识到 Viper 将 ENV 变量视为区分大小写。
Viper 提供了一种机制来确保 ENV 变量是唯一的。通过使用
SetEnvPrefix
,你可以告诉 Viper
在读取环境变量时使用前缀。BindEnv
和
AutomaticEnv
都将使用这个前缀。
1 | SetEnvPrefix("spf") // 将自动转为大写 |
使用 Flags
Viper 具有绑定到标志的能力。
与 BindEnv
类似,该值不是在调用绑定方法时设置的,而是在访问该方法时设置的。这意味着你可以根据需要尽早进行绑定,即使在
init()
函数中也是如此。
对于单个标志,BindPFlag()
方法提供此功能。
1 | serverCmd.Flags().Int("port", 1138, "Port to run Application server on") |
从 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 文件:
1 | { |
Viper 可以通过传入 .
分隔的路径来访问嵌套字段:
1 | GetString("database.metric.host") // 127.0.0.1 |
提取子树
1 | viper.Sub("app.cache") |
反序列化
你可以选择将所有或特定的值解析道结构体、map 等。
有两种方法可以做到这一点:
Unmarshal(rawVal interface{}) : error
UnmarshalKey(key string, rawVal interface{}) : error
示例:
1 | type config struct { |
序列化成字符串
你可能需要将 viper
中保存的所有设置序列化到一个字符串中,而不是将它们写入到一个文件中。你可以将自己喜欢的格式的序列化器与
AllSettings()
返回的配置一起使用。