golang常用库之配置文件解析库-viper使用详解
作者:九卷 发布时间:2024-02-17 18:36:46
golang常用库:gorilla/mux-http路由库使用
golang常用库:配置文件解析库-viper使用
golang常用库:操作数据库的orm框架-gorm基本使用
golang常用库:字段参数验证库-validator使用
一、viper简介
viper 配置管理解析库,是由大神 Steve Francia 开发,他在google领导着 golang 的产品开发,他也是 gohugo.io 的创始人之一,命令行解析库 cobra 开发者。总之,他在golang领域是专家,很牛的一个人。
他的github地址:https://github.com/spf13
viper是一个配置管理的解决方案,它能够从 json,toml,ini,yaml,hcl,env 等多种格式文件中,读取配置内容,它还能从一些远程配置中心读取配置文件,如consul,etcd等;它还能够监听文件的内容变化。
viper的 logo:
二、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,因为它是一个综合文件解析库,包含了上面所有的文件格式解析,是一个集合体,少了配置多个库的烦恼。
三、viper使用
安装viper命令:
go get github.com/spf13/viper
文档: https://github.com/spf13/viper/blob/master/README.md#putting-values-into-viper
通过viper.Set设置值
如果某个键通过viper.Set设置了值,那么这个值读取的优先级最高
viper.Set("mysql.info", "this is mysql info")
设置默认值
https://github.com/spf13/viper/blob/master/README.md#establishing-defaults
viper 支持默认值的设置。如果配置文件、环境变量、远程配置中没有设置键值,就可以通过viper设置一些默认值。
Examples:
viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
读取配置文件
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。
例子1. 读取配置文件
config.toml 配置文件:
# this is a toml
title = "toml exaples"
redis = "127.0.0.1:3300" # redis
[mysql]
host = "192.168.1.1"
ports = 3306
username = "root"
password = "root123456"
viper_toml.go:
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
}
viper.Unmarshal(&config) //将配置文件绑定到config上
fmt.Println("config: ", config, "redis: ", config.Redis)
}
例子2. 读取多个配置文件
在例子1基础上多增加一个json的配置文件,config3.json 配置文件:
{
"redis": "127.0.0.1:33000",
"mysql": {
"port": 3306,
"host": "127.0.0.1",
"username": "root",
"password": "123456"
}
}
viper_multi.go
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("config3")
vjson.SetConfigType("json")
vjson.AddConfigPath(".")
if err := vjson.ReadInConfig(); err != nil {
fmt.Println(err)
return
}
vjson.Unmarshal(&config2)
fmt.Println("read config3.json")
fmt.Println("config: ", config1, "redis: ", config1.Redis)
}
运行:
$ go run viper_multi.go
read config.toml
config: {127.0.0.1:33000 {0 192.168.1.1 root 123456}} redis: 127.0.0.1:33000
read config3.json
config: {127.0.0.1:33000 {0 192.168.1.1 root 123456}} redis: 127.0.0.1:33000
例子3. 读取配置项的值
新建文件夹 item, 在里面创建文件 config.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
}
}
}
item/viper_get_item.go 读取配置项的值
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigName("config")
viper.SetConfigType("json")
viper.AddConfigPath(".")
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"])
}
运行:
$ go run viper_get_item.go
[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
如果把上面的文件config.json写成toml格式,怎么解析? 改成config1.toml:
# toml
toml = "toml example"
redis = "127.0.0.1:33000"
[mysql]
port = 3306
host = "127.0.0.1"
username = "root"
password = "123456"
ports = [5799,6029]
[mysql.metric]
host = "127.0.0.1"
port = 2112
其实解析代码差不多,只需修改2处,
viper.SetConfigName("config") 里的 config 改成 config1 ,
viper.SetConfigType("json")里的 json 改成 toml,其余代码都一样。解析的效果也一样。
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{}
例子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"
}
}
go解析文件,cmd/viper_pflag.go:
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("config")
viper.SetConfigType("json")
viper.AddConfigPath(".")
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 viper_pflag.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 viper_pflag.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命令行的值
例子5:io.Reader中读取值
https://github.com/spf13/viper#reading-config-from-ioreader
viper_ioreader.go
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)
}
例子6:写配置文件
https://github.com/spf13/viper#writing-config-files
新建文件 writer/viper_write_config.go:
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigName("config")
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)
}
}
运行:
$ go run viper_write_config.go
没有任何输出表示生成配置文件成功
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()则会直接覆盖同名文件。
四、参考
viper 文档
golang json库gjson的使用
来源:https://www.cnblogs.com/jiujuan/p/13799976.html


猜你喜欢
- 今天为大家介绍一下python中与class 相关的知识……获取对象的类名python是一门面向对象的语言,对于一切接对象的pyt
- 小孩子刚刚开始学说话的时候,常常是一个字一个字地开始学,比如学说“饺子”,对他/她来讲,似乎有点难度,大人也聪明,于是就简化了,用“饺饺”来
- 1、查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断select * from peoplewhere people
- 子类在多继承中使用MRO机制在Python中,当定义一个类时,可以指定它的父类。一个子类继承了其所有父类的属性和方法,并且可以添加自己特有的
- 你的主页或者你管理的网站有各种密码需要保护,把密码直接放在数据库或者文件中存在不少安全隐患,所以密码加密后存储是最常见的做法。在ASP.NE
- image.func.php<?php require_once('string.func.php');functio
- 1:定义存储过程,用于分隔字符串DELIMITER $$USE `mess`$$DROP PROCEDURE IF EXISTS `spli
- 调用jupyter notebook文件内的函数一种简单方法python开发环境jupyter notebook良好的交互式和模块化受到很多
- 本文主要通过实例介绍了scrapy框架的使用,分享了两个例子,爬豆瓣文本例程 douban 和图片例程 douban_imgs ,具体如下。
- 前言通常我们在业务项目中会借助使用静态代码检查工具来保证代码质量,通过静态代码检查工具我们可以提前发现一些问题,比如变量未定义、类型不匹配、
- 一、添加SVN(1)添加svn插件(2)安装的svn最好是默认路径安装的,不要问我为什么,因为很多软件都是按照默认路径找到bin的表示安装正
- MySQL分页分析原理及提高效率PERCONA PERFORMANCE CONFERENCE 2009上,来自雅虎的几位工程师带来了一篇”E
- 最近公司在研发app,选择了基于Vue框架的vux组件库,现总结在实现上拉刷新功能遇到的坑:1.问题:只刷新一次,解决方法:需要自己手动重置
- Dreamweaver MX 2004的强大功能以及更加完善的人性化设置已经深受大家喜爱。在此笔者就谈
- 目录前言1.什么是锁2.InnoDB存储引擎中的锁2.1锁的类型2.2 一致性非锁定读2.3 一致性锁定读3 锁的算法3.1行锁的3中算法总
- 我们知道了钢琴键盘的音高是其实是有规律的,如下频率翻倍,高一个八度国际基准音:440Hz,钢琴键盘上对应小字一组的la小字一组的la可以看下
- 本文实例为大家分享了python tkinter实现弹窗输入输出的具体代码,供大家参考,具体内容如下代码如下:from tkinter im
- cookielib是一个自动处理cookies的模块,如果我们在使用爬虫等技术的时候需要保存cookie,那么cookielib会让你事半功
- 官方文档介绍链接:append方法介绍DataFrame.append(other, ignore_index=False, verify_
- 用vue2.0开发做兼容时,你可能会发现vue项目在IE11版本浏览器中是空白的。。。看到空白的页面你可能会懵几秒钟,没事,下面我们就来解决