Go 语言编程 — viper 配置管理工具
目录
文章目录
- 目录
- Viper
- Viper 的使用
- 设置默认值
- 显式设置键值
- 从命令行参数中读取配置
- 从环境变量读取配置
- 从配置文件读取配置
- 监视配置文件,并重新读取配置数据
- 读取键值
- 保存配置
- 参考文档
Viper
Viper 是一个 Golang 第三方库,Github:https://github.com/spf13/viper。用于处理 Golang 程序的配置信息,它可以处理多种格式的配置文件。
Viper 支持以下特性:
- 为配置项设置默认值。
- 加载并解析 JSON、TOML、YAML、HCL 或 Java properties 格式的配置文件。
- 可以在命令行参数中读取配置,并指定被覆盖的配置值,常与 Cobra 配合使用。
- 可以从环境变量中读取配置数据。
- 可以从远端配置系统中读取数据,并监视它们(例如:ETCD、Consul)。
- 可以从 Buffer 中读取配置。
- 可以简易地分辨出用户提供的命令行参数或配置文件与默认值的区别。
- 可以监视配置文件的变更、重新读取配置文件。
- 提供了别名系统,可以在不破坏现有代码的前提下实现参数重命名。
- 调用函数设置配置信息
Viper 读取配置信息的优先级顺序,从高到低:
- 显式调用 Set 函数。
- 命令行参数。
- 环境变量。
- 配置文件。
- 远程 key/value 存储系统。
- 默认值
简而言之,Viper 可以让开发者不必担心配置文件的格式,专注与实现创新的业务逻辑。
注:Viper 的配置项的key不区分大小写。
Viper 的使用
设置默认值
默认值不是必须的,但建议使用,如果 Set 函数、命令行参数、环境变量、配置文件、远程配置系统都没有指定时,默认值将起作用。
示例:
viper.SetDefault("name", "xiaoming")
viper.SetDefault("age", "12")
viper.SetDefault("notifyList", []string{"xiaohong", "xiaoli", "xiaowang"})
显式设置键值
如果某个键通过 viper.Set 方法设置了值,那么这个值的优先级最高。
viper.Set("redis.port", 5381)
从命令行参数中读取配置
如果一个键没有通过 viper.Set 方法显式设置值,那么获取时将尝试从命令行参数中读取。Viper 使用 pflag 库来解析命令行选项。
- 指令行
go run test.go --ip=192.168.7.3 --port=3306
- 代码
package mainimport ("fmt""github.com/spf13/pflag""github.com/spf13/viper"
)func main() {// 定义命令行选项pflag.String("ip", "127.0.0.1", "Server running address")pflag.Int64("port", 8080, "Server running port")// 解析命令行选项。pflag.Parse()// 绑定选项到 viper 中。viper.BindPFlags(pflag.CommandLine)fmt.Printf("ip :%s , port:%s", viper.GetString("ip"), viper.GetString("port"))
}
从环境变量读取配置
如果前面方式都没有获取到键值,将尝试从环境变量中读取。
在 Golang 中,通常也会使用 os 包来获取环境变量,比如:
getenv := os.Getenv("JAVA_HOME")
fmt.Print(getenv)
Viper 提供了其特有的一种方式:
// 绑定全部环境变量。
viper.AutomaticEnv()// 验证是否绑定成功。
fmt.Println("GOPATH: ", viper.Get("GOPATH"))// 读取可能读取到的环境变量。
if env := viper.Get("JAVA_HOME"); env == nil {println("error!")
} else {fmt.Printf("%#v\n", env)
}
也支持单独绑定指定的环境变量:
func init() {// 绑定特定的环境变量viper.BindEnv("redis.port")viper.BindEnv("go.path", "GOPATH")
}func main() {fmt.Println("go path: ", viper.Get("go.path"))
}
BindEnv 方法,如果只传入一个参数,则这个参数既表示键名,又表示环境变量名;如果传入两个参数,则第一个参数表示键名,第二个参数表示环境变量名。
还可以通过 viper.SetEnvPrefix 方法设定环境变量的前缀。这样一来,通过 AutomaticEnv 方法和 BindEnv 方法绑定的环境变量,
在使用 Get 方法的时候,Viper 会自动加上这个前缀再从环境变量中进行查找。如果对应的环境变量不存在,Viper 会自动将键名全部转为大写再查找一次。
从配置文件读取配置
从配置文件中读取配置属于从 io.Reader 读取配置一类。从 io.Reader 中读取配置的形式很灵活,来源可以是文件,也可以是程序中生成的字符串,甚至可以从网络连接中读取的字节流(远程配置系统)。
Viper 可以指定从多个路径搜索配置文件,支持 JSON、TOML、YAML、HCL 和 Java properties 格式文件,但目前单个 Viper 实例仅支持单个配置文件。默认的,Viper 不搜索任何路径。所以,路径不是必需的,但建议至少应提供一个路径,如果使用配置文件的话。
示例:
viper.SetConfigName("dbConfig") // 设置配置文件名,不要带后缀。
viper.AddConfigPath("/workspace/path1/") // 第一个搜索路径。
viper.AddConfigPath("/workspace/path2/") // 可以添加多个路径,会根据顺序依次查找。
viper.AddConfigPath(".") // . 表示当前目录。
err := viper.ReadInConfig() // 搜索上述定义的路径,并读取配置数据。if err != nil {panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
读取了配置文件的内容之后,Viper 会自动根据配置文件的类型来进行解析,然后调用 viper.Get 方法获取配置的键值。示例:从 YAML 文件读取配置。
- YAML 文件
userName: "xiaoming"
address: "广州市XXX"
sex: 1
company:name: "xxx"employeeId: 1000department:- "技术部"
- 代码
package mainimport ("fmt""github.com/spf13/viper"
)type UserInfo struct {UserName stringAddress stringSex byteCompany Company
}type Company struct {Name stringEmployeeId intDepartment []interface{}
}func main() {// 构建 Viper 实例v := viper.New()// 设置配置文件名v.SetConfigName("userInfo")// 设置配置文件路径v.AddConfigPath("/root/go/src/webDemo/")// 设置配置文件类型v.SetConfigType("yaml")if err := v.ReadInConfig();err != nil {fmt.Printf("err:%s\n",err)}fmt.Printf("userName:%s sex:%s company.name:%s \n", v.Get("userName"), v.Get("sex"), v.Get("company.name"))// 反序列化为 Struct 类型变量var userInfo UserInfoif err := v.Unmarshal(&userInfo) ; err != nil{fmt.Printf("err:%s",err)}fmt.Println(userInfo)
}
注:通常的,配置文件的信息在代码中有两种存放的形式:
- 直接解析为 key/value Map 类型变量。
- 显式的反序列化为 Struct 类型变量。
监视配置文件,并重新读取配置数据
Viper 支持动态更新配置文件,使得应用程序具有运行时读取配置文件的能力,因此不需要重启服务器,就能让配置生效。需要调用 Viper 实例的 WatchConfig() 函数。Viper 使用 fsnotify 库来实现监听文件修改的功能。
示例:
package mainimport ("fmt""log""time""github.com/spf13/viper"
)func main() {viper.SetConfigName("config")viper.SetConfigType("toml")viper.AddConfigPath(".")err := viper.ReadInConfig()if err != nil {log.Fatal("read config failed: %v", err)}viper.WatchConfig()fmt.Println("redis port before sleep: ", viper.Get("redis.port"))time.Sleep(time.Second * 10)fmt.Println("redis port after sleep: ", viper.Get("redis.port"))
}
只需要调用 viper.WatchConfig,Viper 就会自动监听配置修改。如果有修改,则重新加载的配置。
上述示例中,我们先打印 redis.port 的值,然后 Sleep 10s。在这期间修改配置中 redis.port 的值,Sleep 结束后再次打印。发现打印出修改后的值:
redis port before sleep: 7381
redis port after sleep: 73810
另外,也可以为配置修改的动作指定一个回调函数来获得变动的通知:
viper.OnConfigChange(func(e fsnotify.Event) {fmt.Printf("Config file:%s Op:%s\n", e.Name, e.Op)
})
这样文件修改时就会执行这个回调。
读取键值
在各种配置源中读取了配置信息之后,就可以通过 Viper 实例提供的各种方法进行键值的读取了。
值得注意的是,Viper 提供的 Get 方法返回的是一个 interface{} 类型值,使用起来会有所不便。所以,Viper 还提供了 GetType 系列方法,用于返回指定类型的值。如果指定的键不存在或类型不正确,GetType 方法返回对应类型的零值。其中,Type 可以为:
- Bool
- Float64
- Int
- String
- Time
- Duration
- IntSlice
- StringSlice
如果要判断某个键是否存在,可以使用 IsSet 方法。另外,GetStringMap 和 GetStringMapString 方法可以直接以 Map 返回某个键下面所有的键值对,前者返回 map[string]interface{},后者返回 map[string]string。AllSettings 方法则以 map[string]interface{} 返回所有设置。
示例
- config.toml 文件
[server]
protocols = ["http", "https", "port"]
ports = [10000, 10001, 10002]
timeout = 3s
- 代码
func main() {viper.SetConfigName("config")viper.SetConfigType("toml")viper.AddConfigPath(".")err := viper.ReadInConfig()if err != nil {log.Fatal("read config failed: %v", err)}fmt.Println("protocols: ", viper.GetStringSlice("server.protocols"))fmt.Println("ports: ", viper.GetIntSlice("server.ports"))fmt.Println("timeout: ", viper.GetDuration("server.timeout"))fmt.Println("mysql ip: ", viper.GetString("mysql.ip"))fmt.Println("mysql port: ", viper.GetInt("mysql.port"))if viper.IsSet("redis.port") {fmt.Println("redis.port is set")} else {fmt.Println("redis.port is not set")}fmt.Println("mysql settings: ", viper.GetStringMap("mysql"))fmt.Println("redis settings: ", viper.GetStringMap("redis"))fmt.Println("all settings: ", viper.AllSettings())
}
- 结果
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。
保存配置
当我们想要将程序中生成的配置,或者所做的修改保存下来,就可以使用 Viper 提供的接口:
- WriteConfig:将当前的 Viper 配置写到预定义路径,如果没有预定义路径,则返回错误。否则,将会覆盖当前配置。
- SafeWriteConfig:与上面功能一样,但是如果配置文件存在,则不覆盖;
- WriteConfigAs:保存配置到指定路径,如果文件存在,则覆盖;
- SafeWriteConfig:与上面功能一样,但是如果配置文件存在,则不覆盖。
示例:
package mainimport ("log""github.com/spf13/viper"
)func main() {viper.SetConfigName("config")viper.SetConfigType("toml")viper.AddConfigPath(".")viper.Set("app_name", "awesome web")viper.Set("log_level", "DEBUG")viper.Set("mysql.ip", "127.0.0.1")viper.Set("mysql.port", 3306)viper.Set("mysql.user", "root")viper.Set("mysql.password", "123456")viper.Set("mysql.database", "awesome")viper.Set("redis.ip", "127.0.0.1")viper.Set("redis.port", 6381)err := viper.SafeWriteConfig()if err != nil {log.Fatal("write config failed: ", err)}
}
保存下来的配置文件内容如下:
app_name = "awesome web"
log_level = "DEBUG"[mysql]database = "awesome"ip = "127.0.0.1"password = "123456"port = 3306user = "root"[redis]ip = "127.0.0.1"port = 6381
参考文档
https://www.cnblogs.com/rickiyang/p/11074161.html
Go 语言编程 — viper 配置管理工具相关推荐
- linux c语言工具,Linux下C语言编程环境的工具.doc
Linux下C语言编程环境的工具 Linux下C语言编程环境的工具 Linux下C语言编程环境的工具 要想在Linux下进行C语言编程,首先得搭建好一个编程环境.这里分别说明一下几个非常有用的软件包. ...
- 易语言宝盒,易语言编程学习者必备工具
1.收集很多网上流行经典的易语言教程和一些常用的工具,易语言模块,易语言支持库.一键下载,双通道资源,保证资源不会失效. 2.支持在线观看,减少下载资源而浪费易友的时间! 3.支持一键下载宝盒上所有已 ...
- 好用的C语言编程软件!工具都没有,怎么用技术改变世界呢!
好用的C语言编程软件 1.VS(Visual Studio) VS是目前最流行的windows平台应用程序的集成开发环境,由于大部分同学使用的都是Windows操作系统,VS对于大家来说是极为合适的, ...
- Go 语言编程 — go-swagger OpenAPI 工具
目录 文章目录 目录 go-swagger Generate a spec from source Generate an API server 参考文档 go-swagger go-swagger ...
- 使用Qt学习C语言编程2(加入工具链)
按下图配置工具链 一定要动手操作,在做的过程中理解工具链的重要性和作用. 在study1_CreatePureCPrj项目中新增一个模块,用于封装led(led\led.h led\led.c) 更改 ...
- Go 语言编程 — Cobra 指令行工具
目录 文章目录 目录 Cobra(眼镜蛇) Cobra 的核心概念 Cobra 的使用 初始化应用程序的项目框架 main.go 生成应用程序的子命令(SubCmd) 实现 Command 的功能 为 ...
- Go 语言编程 — 高级数据类型 — 结构体
目录 文章目录 目录 结构体 访问结构体成员 向函数传递结构体 结构体指针 结构体标签(Struct Tag) 结构体 Golang 中,结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.与 ...
- C 语言编程 — GCC 工具链
目录 文章目录 目录 文章目录 GCC 工具链 GCC 编译器 GCC 的常用指令选项 GCC 所遵循的部分约定规则 Binutils C 运行时库 Clang 和 LLVM 文章目录 <C 语 ...
- Perl/CGI脚本语言编程学习资料及开发工具下载大全
Practical Extraction and Report Language Perl 最初的设计者为拉里·沃尔(Larry Wall),它于1987年12月18日发表.Perl借取了C.sed. ...
最新文章
- C#编码标准--编码习惯
- pythonide机制_强化vim打造python的IDE
- mysql游标事例_MySQL游标语法实例
- apache+php32位平台安装
- 数据挖掘 pandas基础入门之操作
- 关于F5 排错的简单介绍之一
- Win11系统无法安装GPT分区的解决方法
- 2020流行的液态风格PNG免扣素材,竟然被我找到了!
- PowerVim - 强大的vim配置
- Talib技术因子详解(一)
- android 支付宝 记账本,使用支付宝记账----懒人的最佳记账模式
- excel图形二(雷达图、瀑布图甘特图、旭日图、树状图、组合图)与动态图
- element-ui 删除input框尾部默认图标和获取焦点边框高亮问题
- 事业单位计算机专业能力测试考什么内容,事业单位职业能力测试考什么
- C++中string类函数常用函数大全
- Mac新手入门以及常用软件推荐
- 图像低频、高频信息的理解
- 超松弛迭代法(SOR)的Python实现
- Docker 安装 MySQL(借鉴菜鸟教程)
- 手机短信息被误删怎么办?最常用的恢复小技巧
热门文章
- Swift2.0语言教程之类的嵌套与可选链接
- Playmaker Input篇教程之引入的核心概念
- java 如何开启一个线程_Java-开启一个新的线程
- mysql城市联动表怎么建_MVC4.0搭建的省市县三联动,包含数据库
- ue4 改变枢轴位置_【UE4地形】轻松实现UE4自动地貌和自动植被分布
- requests payload_python+Requests接口自动化测试之传递 URL 参数
- java中let_java Signleton模式详解及示例代码
- 脑-脑接口:人类大脑利用意念控制老鼠走迷宫
- 听说有人解决了三体问题,我去做了一点微小的调查
- 专访英特尔戴金权 | AI和大数据正在这样重塑英特尔