Viper
viper
我们将使用 Viper 包来作为yaml 和 config 信息的基础库。
Viper 是 Go 生态圈里一个很知名的项目,在 GitHub 上有接近 1.8 万个 star。
它支持以下特性:
- 设置默认值(存入时设置)
- 支持格式配置信息的格式包括
JSON、TOML、YAML、HCL、envfile和Java properties - 实时监控和重新读取配置文件(可选)
- 从环境变量中读取
- 从远程配置系统(etcd 或 Consul)读取并监控配置变化
- 从命令行参数读取配置
- 从 buffer 读取配置
- 显式配置值
Viper 的功能比较丰富,这意味着面对多变的需求时我们可以很灵活。且作为明星项目,严格测试、使用广泛,很适合作为我们的配置模块的底层包。
安装
1 | |
viper基本用法
config.json
1 | |
config.yaml
1 | |
建立默认值
一个好的配置系统应该支持默认值。键不需要默认值,但如果没有通过配置文件、环境变量、远程配置或命令行标志(flag)设置键,则默认值非常有用。
例如:
1 | |
读取配置文件
Viper需要最少知道在哪里查找配置文件的配置。Viper支持JSON、TOML、YAML、HCL、envfile和Java properties格式的配置文件。Viper可以搜索多个路径,但目前单个Viper实例只支持单个配置文件。Viper不默认任何配置搜索路径,将默认决策留给应用程序。
下面是一个如何使用Viper搜索和读取配置文件的示例。不需要任何特定的路径,但是至少应该提供一个配置文件预期出现的路径。
1 | |
在加载配置文件出错时,你可以像下面这样处理找不到配置文件的特定情况:
1 | |
注意[自1.6起]: 你也可以有不带扩展名的文件,并以编程方式指定其格式。对于位于用户$HOME目录中的配置文件没有任何扩展名,如.bashrc。
这里补充两个问题供读者解答并自行验证
当你使用如下方式读取配置时,viper会从./conf目录下查找任何以config为文件名的配置文件,如果同时存在./conf/config.json和./conf/config.yaml两个配置文件的话,viper会从哪个配置文件加载配置呢?
1 | |
在上面两个语句下搭配使用viper.SetConfigType("yaml")指定配置文件类型可以实现预期的效果吗?
使用环境变量
Viper完全支持环境变量。这使Twelve-Factor App开箱即用。有五种方法可以帮助与ENV协作:
AutomaticEnv()BindEnv(string...) : errorSetEnvPrefix(string)SetEnvKeyReplacer(string...) *strings.ReplacerAllowEmptyEnv(bool)
使用ENV变量时,务必要意识到Viper将ENV变量视为区分大小写。
Viper提供了一种机制来确保ENV变量是惟一的。通过使用SetEnvPrefix,你可以告诉Viper在读取环境变量时使用前缀。BindEnv和AutomaticEnv都将使用这个前缀。
BindEnv使用一个或两个参数。第一个参数是键名称,第二个是环境变量的名称。环境变量的名称区分大小写。如果没有提供ENV变量名,那么Viper将自动假设ENV变量与以下格式匹配:前缀+ “_” +键名全部大写。当你显式提供ENV变量名(第二个参数)时,它 不会 自动添加前缀。例如,如果第二个参数是“id”,Viper将查找环境变量“ID”。
在使用ENV变量时,需要注意的一件重要事情是,每次访问该值时都将读取它。Viper在调用BindEnv时不固定该值。
AutomaticEnv是一个强大的助手,尤其是与SetEnvPrefix结合使用时。调用时,Viper会在发出viper.Get请求时随时检查环境变量。它将应用以下规则。它将检查环境变量的名称是否与键匹配(如果设置了EnvPrefix)。
SetEnvKeyReplacer允许你使用strings.Replacer对象在一定程度上重写 Env 键。如果你希望在Get()调用中使用-或者其他什么符号,但是环境变量里使用_分隔符,那么这个功能是非常有用的。可以在viper_test.go中找到它的使用示例。
或者,你可以使用带有NewWithOptions工厂函数的EnvKeyReplacer。与SetEnvKeyReplacer不同,它接受StringReplacer接口,允许你编写自定义字符串替换逻辑。
默认情况下,空环境变量被认为是未设置的,并将返回到下一个配置源。若要将空环境变量视为已设置,请使用AllowEmptyEnv方法。
Env 示例:
1 | |
从Viper获取值
在Viper中,有几种方法可以根据值的类型获取值。存在以下功能和方法:
Get(key string) : interface{}GetBool(key string) : boolGetFloat64(key string) : float64GetInt(key string) : intGetIntSlice(key string) : []intGetString(key string) : stringGetStringMap(key string) : map[string]interface{}GetStringMapString(key string) : map[string]stringGetStringSlice(key string) : []stringGetTime(key string) : time.TimeGetDuration(key string) : time.DurationIsSet(key string) : boolAllSettings() : map[string]interface{}
需要认识到的一件重要事情是,每一个Get方法在找不到值的时候都会返回零值。为了检查给定的键是否存在,提供了IsSet()方法。
例如:
1 | |