Go语言配置解析库viper的使用指南
作者:zsx_yiyiyi
1、viper简介
viper 配置管理解析库,是由大神 Steve Francia 开发,他在google领导着 golang 的产品开发,他也是 gohugo.io的创始人之一,命令行解析库 cobra 开发者。
viper是一个配置管理的解决方案,它能够从 json,toml,ini,yaml,hcl,env 等多种格式文件中,读取配置内容,它还能从一些远程配置中心读取配置文件,如consul,etcd等;它还能够监听文件的内容变化。
2、viper功能介绍
- 读取 json,toml,ini,yaml,hcl,env 等格式的文件内容
- 读取远程配置文件,如 consul,etcd 等和监控配置文件变化
- 读取命令行 flag 的值
- 从 buffer 中读取值
配置文件又可以分为不同的环境,比如dev,test,prod等。
viper 可以帮助你专注配置文件管理。
viper 读取配置文件的优先顺序,从高到低,如下:
- 显式设置的Set函数
- 命令行参数
- 环境变量
- 配置文件
- 远程k-v 存储系统,如consul,etcd等
- 默认值
Viper 配置key是不区分大小写的。
其实,上面的每一种文件格式,都有一些比较有名的解析库,如:
toml:https://github.com/BurntSushi/toml
json:json的解析库比较多,下面列出几个常用的
- https://github.com/json-iterator/go
- https://github.com/mailru/easyjson
- https://github.com/bitly/go-simplejson
- https://github.com/tidwall/gjson
ini:https://github.com/go-ini/ini 等等单独文件格式解析库。
但是为啥子要用viper,因为它是一个综合文件解析库,包含了上面所有的文件格式解析,是一个集合体,少了配
置多个库的烦恼。
3、viper使用
安装viper命令:
go get github.com/spf13/viper
文档:
https://github.com/spf13/viper/blob/master/README.md#putting-values-into-viper
3.1 通过viper.Set设置值
如果某个键通过viper.Set设置了值,那么这个值读取的优先级最高
viper.Set("mysql.info", "this is mysql info")
3.2 设置默认值
https://github.com/spf13/viper/blob/master/README.md#establishing-defaults
viper 支持默认值的设置。如果配置文件、环境变量、远程配置中没有设置键值,就可以通过viper设置一些默认
值。
viper.SetDefault("ContentDir", "content") viper.SetDefault("LayoutDir", "layouts") viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
3.3 读取配置文件
https://github.com/spf13/viper/blob/master/README.md#reading-config-files
读取配置文件要求:最少要知道从哪个位置查找配置文件。用户一定要设置这个路径。
viper可以从多个路径搜索配置文件,单个viper实例只支持单个配置文件。 viper本身没有设置默认的搜索路径,
需要用户自己设置默认路径。
viper搜索和读取配置文件例子片段:
viper.SetConfigName("config") // 配置文件的文件名,没有扩展名,如.yaml,.toml这样的扩展名 viper.SetConfigType("yaml") // 设置扩展名,在这里设置文件的扩展名,另外如果配置文件的名称没有扩展名,则需要配置这个选项 viper.AddConfigPath("/etc/appname/") // 查找配置文件所在路径 viper.AddConfigPath("$HOME/.appname") // 多次调用AddConfigPath,可以添加多个搜索路径 viper.AddConfigPath(".") // 还可以在工作目录中搜索配置文件 err := viper.ReadInConfig() // 搜索并读取配置文件 if err != nil { // 处理错误 panic(fmt.Errorf("Fatal error config file: %s \n", err)) }
说明: 这里执行viper.ReadInConfig()之后,viper才能确定到底用哪个文件,viper按照上面的
AddConfigPath() 进行搜索,找到第一个名为 config.ext (这里的ext代表扩展名: 如
json,toml,yaml,yml,ini,prop 等扩展名) 的文件后即停止搜索。
如果有多个名称为config的配置文件,viper怎么搜索呢?它会按照如下顺序搜索
- config.json
- config.toml
- config.yaml
- config.yml
- config.properties (这种一般是java中的配置文件名)
- config.props (这种一般是java中的配置文件名)
你还可以处理一些特殊情况:
if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { // 配置文件没有找到; 如果需要可以忽略 } else { // 查找到了配置文件但是产生了其它的错误 } } // 查找到配置文件并解析成功
注意[自1.6起]: 你也可以有不带扩展名的文件,并以编程方式指定其格式。对于位于用户$HOME目录中的
配置文件没有任何扩展名,如.bashrc。
3.4 例子1:读取配置文件
config.toml 配置文件:
title = "toml exaples" redis = "127.0.0.1:3300" [mysql] host = "192.168.1.1" ports = 3306 username = "root" password = "root123456"
package main import( "fmt" "github.com/spf13/viper" ) // 读取配置文件config type Config struct { Redis string MySQL MySQLConfig } type MySQLConfig struct { Port int Host string Username string Password string } func main() { // 把配置文件读取到结构体上 var config Config viper.SetConfigName("config") viper.AddConfigPath(".") err := viper.ReadInConfig() if err != nil { fmt.Println(err) return } //将配置文件绑定到config上 viper.Unmarshal(&config) fmt.Println("config: ", config, "redis: ", config.Redis) }
输出
config: {127.0.0.1:3300 {0 192.168.1.1 root root123456}} redis: 127.0.0.1:3300
3.5 例子2:读取多个配置文件
在例子1基础上多增加一个json的配置文件,config1.json 配置文件:
{ "redis": "127.0.0.1:33000", "mysql": { "port": 3306, "host": "127.0.0.1", "username": "root", "password": "123456" } }
读取多个配置文件:
package main import ( "fmt" "github.com/spf13/viper" ) type Config struct { Redis string MySQL MySQLConfig } type MySQLConfig struct { Port int Host string Username string Password string } func main() { // 读取 toml 配置文件 var config1 Config vtoml := viper.New() vtoml.SetConfigName("config") vtoml.SetConfigType("toml") vtoml.AddConfigPath(".") if err := vtoml.ReadInConfig(); err != nil { fmt.Println(err) return } vtoml.Unmarshal(&config1) fmt.Println("read config.toml") fmt.Println("config: ", config1, "redis: ", config1.Redis) // 读取 json 配置文件 var config2 Config vjson := viper.New() vjson.SetConfigName("config1") vjson.SetConfigType("json") vjson.AddConfigPath(".") if err := vjson.ReadInConfig(); err != nil { fmt.Println("bb",err) return } vjson.Unmarshal(&config2) fmt.Println("read config1.json") fmt.Println("config: ", config2, "redis: ", config2.Redis) }
运行:
# 输出
read config.toml
config: {127.0.0.1:3300 {0 192.168.1.1 root root123456}} redis: 127.0.0.1:3300
read config1.json
config: {127.0.0.1:33000 {3306 127.0.0.1 root 123456}} redis: 127.0.0.1:33000
3.6 例子3:读取配置项的值
新建文件夹 item, 在里面创建文件 config2.json,内容如下:
{ "redis": "127.0.0.1:33000", "mysql": { "port": 3306, "host": "127.0.0.1", "username": "root", "password": "123456", "ports": [ 5799, 6029 ], "metric": { "host": "127.0.0.1", "port": 2112 } } }
读取配置项的值:
package main import ( "fmt" "github.com/spf13/viper" ) func main() { viper.SetConfigName("config2") viper.SetConfigType("json") viper.AddConfigPath("item") // 根据上面配置加载文件 err := viper.ReadInConfig() if err != nil { fmt.Println(err) return } host := viper.Get("mysql.host") username := viper.GetString("mysql.username") port := viper.GetInt("mysql.port") portsSlice := viper.GetIntSlice("mysql.ports") metricPort := viper.GetInt("mysql.metric.port") redis := viper.Get("redis") mysqlMap := viper.GetStringMapString("mysql") if viper.IsSet("mysql.host") { fmt.Println("[IsSet()]mysql.host is set") } else { fmt.Println("[IsSet()]mysql.host is not set") } fmt.Println("mysql - host: ", host, ", username: ", username, ", port: ", port) fmt.Println("mysql ports :", portsSlice) fmt.Println("metric port: ", metricPort) fmt.Println("redis - ", redis) fmt.Println("mysqlmap - ", mysqlMap, ", username: ", mysqlMap["username"]) }
运行:
# 输出
[IsSet()]mysql.host is set
mysql - host: 127.0.0.1 , username: root , port: 3306
mysql ports : [5799 6029]
metric port: 2112
redis - 127.0.0.1:33000
mysqlmap - map[host:127.0.0.1 metric: password:123456 port:3306 ports: username
:root] , username: root
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{}
3.7 例子4:读取命令行的值
新建文件夹 cmd,然后cmd文件夹里新建config.json文件:
{ "redis":{ "port": 3301, "host": "127.0.0.1" }, "mysql": { "port": 3306, "host": "127.0.0.1", "username": "root", "password": "123456" } }
读取命令行的值:
package main import ( "fmt" "github.com/spf13/pflag" "github.com/spf13/viper" ) func main() { pflag.Int("redis.port", 3302, "redis port") viper.BindPFlags(pflag.CommandLine) pflag.Parse() viper.SetConfigName("config3") viper.SetConfigType("json") viper.AddConfigPath("cmd") // 根据上面配置加载文件 err := viper.ReadInConfig() if err != nil { fmt.Println(err) return } host := viper.Get("mysql.host") username := viper.GetString("mysql.username") port := viper.GetInt("mysql.port") redisHost := viper.GetString("redis.host") redisPort := viper.GetInt("redis.port") fmt.Println("mysql - host: ", host, ", username: ", username, ", port: ", port) fmt.Println("redis - host: ", redisHost, ", port: ", redisPort) }
1、不加命令行参数运行:
$ go run 004.go
# 输出
mysql - host: 127.0.0.1 , username: root , port: 3306
redis - host: 127.0.0.1 , port: 3301
说明:redis.port 的值是 3301,是 config.json 配置文件里的值。
2、加命令行参数运行
$ go run 004.go --redis.port 6666
# 输出
mysql - host: 127.0.0.1 , username: root , port: 3306
redis - host: 127.0.0.1 , port: 6666
说明:加了命令行参数 --redis.port 6666,这时候redis.port输出的值为 6666,读取的是cmd命令行的值。
3.8 例子5:io.Reader中读取值
https://github.com/spf13/viper#reading-config-from-ioreader
package main import ( "bytes" "fmt" "github.com/spf13/viper" ) func main() { viper.SetConfigType("yaml") var yaml = []byte(` Hacker: true name: steve hobbies: - skateboarding - snowboarding - go clothing: jacket: leather trousers: denim age: 35 eyes : brown beard: true `) err := viper.ReadConfig(bytes.NewBuffer(yaml)) if err != nil { fmt.Println(err) return } hacker := viper.GetBool("Hacker") hobbies := viper.GetStringSlice("hobbies") jacket := viper.Get("clothing.jacket") age := viper.GetInt("age") fmt.Println("Hacker: ", hacker, ",hobbies: ", hobbies, ",jacket: ", jacket, ",age: ", age) }
输出
Hacker: true ,hobbies: [skateboarding snowboarding go] ,jacket: leather ,age: 35
3.9 例子6:写配置文件
https://github.com/spf13/viper#writing-config-files
package main import ( "fmt" "github.com/spf13/viper" ) func main() { viper.SetConfigName("config4") viper.SetConfigType("yaml") viper.AddConfigPath(".") viper.Set("yaml", "this is a example of yaml") viper.Set("redis.port", 4405) viper.Set("redis.host", "127.0.0.1") viper.Set("mysql.port", 3306) viper.Set("mysql.host", "192.168.1.0") viper.Set("mysql.username", "root123") viper.Set("mysql.password", "root123") if err := viper.WriteConfig(); err != nil { fmt.Println(err) } }
没有任何输出表示生成配置文件成功:
mysql:
host: 192.168.1.0
password: root123
port: 3306
username: root123
redis:
host: 127.0.0.1
port: 4405
yaml: this is a example of yaml
WriteConfig() 和 SafeWriteConfig() 区别:
如果待生成的文件已经存在,那么SafeWriteConfig()就会报错:
Config File "config.yaml" Already Exists,
而WriteConfig()则会直接覆盖同名文件。
以上就是Go语言配置解析库viper的使用指南的详细内容,更多关于Go viper的资料请关注脚本之家其它相关文章!