首页 体育 教育 财经 社会 娱乐 军事 国内 科技 互联网 房产 国际 女人 汽车 游戏

Go 每日一库之 viper

2020-05-21

上一篇文章介绍 cobra 的时分提到了 viper ,今日咱们就来介绍一下这个库。

viper 是一个装备解决方案,具有丰厚的特性:

io.Reader

装置:

$ go get github.com/spf13/viper

运用:

package main
import  {
 viper.SetConfigName
 viper.SetConfigType
 viper.AddConfigPath
 viper.SetDefault
 err := viper.ReadInConfig
 if err != nil {
 log.Fatal
 fmt.Println)
 fmt.Println)
 fmt.Println)
 fmt.Println)
 fmt.Println)
 fmt.Println)
 fmt.Println)
 fmt.Println)
 fmt.Println)
}

咱们运用之前 Go 每日一库之 go-ini 一文中运用的装备,不过改为 toml 格局。

toml 的语法很简略,快速入门请看 learn X in Y minutes 。

app_name = awesome web 
# possible values: DEBUG, INFO, WARNING, ERROR, FATAL
log_level = DEBUG 
[mysql]
ip = 127.0.0.1 
port = 3306
user = dj 
password = 123456
database = awesome 
[redis]
ip = 127.0.0.1 
port = 7381

viper 的运用十分简略,它需求很少的设置。设置文件名、装备类型和查找途径,然后调用 ReadInConfig 。

viper会主动依据类型来读取装备。运用时调用 viper.Get 办法获取键值。

编译、运转程序:

awesome web
DEBUG
mysql ip: 127.0.0.1
mysql port: 3306
mysql user: dj
mysql password: 123456
mysql database: awesome
redis ip: 127.0.0.1
redis port: 7381

有几点需求留意:

section.key
viper.SetDefault

viper 供给了多种方式的读取办法。在上面的比如中,咱们看到了 Get 办法的用法。 Get 办法回来一个 interface{} 的值,运用有所不方便。

GetType 系列办法能够回来指定类型的值。

其间,Type 能够为 Bool/Float64/Int/String/Time/Duration/IntSlice/StringSlice 。

可是请留意, 假如指定的键不存在或类型不正确, GetType 办法回来对应类型的零值 。

假如要判别某个键是否存在,运用 IsSet 办法。

别的, GetStringMap 和 GetStringMapString 直接以 map 回来某个键下面一切的键值对,前者回来 map[string]interface{} ,后者回来 map[string]string 。

AllSettings 以 map[string]interface{} 回来一切设置。

// 省掉包名和 import 部分
func main {
 viper.SetConfigName
 viper.SetConfigType
 viper.AddConfigPath
 err := viper.ReadInConfig
 if err != nil {
 log.Fatal
 fmt.Println)
 fmt.Println)
 fmt.Println)
 fmt.Println)
 fmt.Println)
 if viper.IsSet {
 fmt.Println
 } else {
 fmt.Println
 fmt.Println)
 fmt.Println)
 fmt.Println)
}

咱们在装备文件 config.toml 中添加 protocols 和 ports 装备:

[server]
protocols = [ http , https , port ]
ports = [10000, 10001, 10002]
timeout = 3s

编译、运转程序,输出:

protocols: [http https port]
ports: [10000 10001 10002]
timeout: 3s
mysql ip: 127.0.0.1
mysql port: 3306
redis.port is set
mysql settings: map[database:awesome ip:127.0.0.1 password:123456 port:3306 user:dj]
redis settings: map[ip:127.0.0.1 port:7381]
all settings: map[app_name:awesome web log_level:DEBUG mysql:map[database:awesome ip:127.0.0.1 password:123456 port:3306 user:dj] redis:map[ip:127.0.0.1 port:7381] server:map[ports:[10000 10001 10002] protocols:[http https port]]]

假如将装备中的 redis.port 注释掉,将输出 redis.port is not set 。

上面的示例中还演示了怎么运用 time.Duration 类型,只要是 time.ParseDuration 承受的格局都能够,例如 3s 、 2min 、 1min30s 等。

viper 支撑在多个当地设置,运用下面的次序顺次读取:

Set

假如某个键经过 viper.Set 设置了值,那么这个值的优先级最高。

viper.Set

假如将上面这行代码放到程序中,运转程序,输出的 redis.port 将是 5381。

假如一个键没有经过 viper.Set 显现设置值,那么获取时将测验从命令行选项中读取。

假如有,优先运用。viper 运用 pflag 库来解析选项。

咱们首先在 init 办法中界说选项,而且调用 viper.BindPFlags 绑定选项到装备中:

func init {
 pflag.Int
 // 绑定命令行
 viper.BindPFlags
}

然后,在 main 办法最初处调用 pflag.Parse 解析选项。

编译、运转程序:

$ ./main.exe --redis.port 9381
awesome web
DEBUG
mysql ip: 127.0.0.1
mysql port: 3306
mysql user: dj
mysql password: 123456
mysql database: awesome
redis ip: 127.0.0.1
redis port: 9381

怎么不传入选项:

$ ./main.exe
awesome web
DEBUG
mysql ip: 127.0.0.1
mysql port: 3306
mysql user: dj
mysql password: 123456
mysql database: awesome
redis ip: 127.0.0.1
redis port: 7381

留意,这儿并不会运用选项 redis.port 的默认值。

可是,假如经过下面的办法都无法取得键值,那么回来选项默认值。试试注释掉装备文件中 redis.port 看看作用。

假如前面都没有获取到键值,将测验从环境变量中读取。咱们既能够一个个绑定,也能够主动悉数绑定。

在 init 办法中调用 AutomaticEnv 办法绑定悉数环境变量:

func init {
 // 绑定环境变量
 viper.AutomaticEnv
}

为了验证是否绑定成功,咱们在 main 办法中将环境变量 GOPATH 打印出来:

func main {
 // 省掉部分代码
 fmt.Println)
}

经过 体系 - 高档设置 - 新建 创立一个名为 redis.port 的环境变量,值为 10381。

运转程序,输出的 redis.port 值为 10381,而且输出中有 GOPATH 信息。

也能够独自绑定环境变量:

func init {
 // 绑定环境变量
 viper.BindEnv
 viper.BindEnv
func main {
 // 省掉部分代码
 fmt.Println)
}

调用 BindEnv 办法,假如只传入一个参数,则这个参数既表明键名,又表明环境变量名。

假如传入两个参数,则第一个参数表明键名,第二个参数表明环境变量名。

还能够经过 viper.SetEnvPrefix 办法设置环境变量前缀,这样一来,经过 AutomaticEnv 和一个参数的 BindEnv 绑定的环境变量,

在运用 Get 的时分,viper 会主动加上这个前缀再从环境变量中查找。

假如对应的环境变量不存在,viper 会主动将键名悉数转为大写再查找一次。所以,运用键名 gopath 也能读取环境变量 GOPATH 的值。

假如经过前面的途径都没能找到该键,viper 接下来会测验从装备文件中查找。

为了防止环境变量的影响,需求删去 redis.port 这个环境变量。

看中的示例。

在上面的一节,咱们现已看到了怎么设置默认值,这儿就不赘述了。

viper 支撑从 io.Reader 中读取装备。这种方式很灵敏,来历能够是文件,也能够是程序中生成的字符串,乃至能够从网络连接中读取的字节省。

package main
import  {
 viper.SetConfigType
 tomlConfig := []byte)
 if err != nil {
 log.Fatal
 fmt.Println)
}

viper 支撑将装备 Unmarshal 到一个结构体中,为结构体中的对应字段赋值。

package main
import  {
 viper.SetConfigName
 viper.SetConfigType
 viper.AddConfigPath
 err := viper.ReadInConfig
 if err != nil {
 log.Fatal
 var c Config
 viper.Unmarshal
 fmt.Println
}

编译,运转程序,输出:

{127.0.0.1 3306 dj 123456 awesome}

有时分,咱们想要将程序中生成的装备,或许所做的修正保存下来。viper 供给了接口!

WriteConfig
SafeWriteConfig
WriteConfigAs
SafeWriteConfig

下面咱们经过程序生成一个 config.toml 装备:

package main
import  {
 viper.SetConfigName
 viper.SetConfigType
 viper.AddConfigPath
 viper.Set
 viper.Set
 viper.Set
 viper.Set
 viper.Set
 viper.Set
 viper.Set
 viper.Set
 viper.Set
 err := viper.SafeWriteConfig
 if err != nil {
 log.Fatal
}

编译、运转程序,生成的文件如下:

app_name = awesome web 
log_level = DEBUG 
[mysql]
 database = awesome 
 ip = 127.0.0.1 
 password = 123456 
 port = 3306
 user = root 
[redis]
 ip = 127.0.0.1 
 port = 6381

viper 能够监听文件修正,热加载装备。因而不需求重启服务器,就能让装备收效。

package main
import  {
 viper.SetConfigName
 viper.SetConfigType
 viper.AddConfigPath
 err := viper.ReadInConfig
 if err != nil {
 log.Fatal
 viper.WatchConfig
 fmt.Println)
 time.Sleep
 fmt.Println)
}

只需求调用 viper.WatchConfig ,viper 会主动监听装备修正。假如有修正,从头加载的装备。

上面程序中,咱们先打印 redis.port 的值,然后 Sleep 10s。在这期间修正装备中 redis.port 的值, Sleep 完毕后再次打印。

发现打印出修正后的值:

redis port before sleep: 7381
redis port after sleep: 73810

别的,还能够为装备修正添加一个回调:

viper.OnConfigChange {
 fmt.Printf
})

这样文件修正时会履行这个回调。

viper 运用 fsnotify 这个库来完成监听文件修正的功用。

完好示例代码见 GitHub 。

我的博客

欢迎重视我的微信大众号,一同学习,一同前进~

本文由博客一文多发渠道 OpenWrite 发布!

热门文章

随机推荐

推荐文章