go viper
一 概述
Viper是Go应用程序的完整配置解决方案,它旨在在应用程序中工作,并可以处理所有类型的配置需求和格式。
它支持特性:
- 设置默认值
- 从JSON,TOML,YAML,HCL和Java属性配置文件中读取
- 实时观看和重新读取配置文件(可选)
- 从环境变量中读取
- 从远程配置系统(etcd或Consul)读取,并观察变化
- 从命令行标志读取
- 从缓冲区读取
- 设置显式值
Viper读取配置信息的优先级顺序,从高到低,如下:
- 显式调用Set函数
- 命令行参数
- 环境变量
- 配置文件
- key/value 存储系统
- 默认值
备注: Viper 的配置项的key不区分大小写。
二 安装
go get github.com/spf13/viper
三 使用
3.1 viper 数据获取
3.1.1 从命令行参数获取数据
如果一个键没有通过viper.Set显示设置值,那么获取时将尝试从命令行选项中读取。如果有,优先使用。viper 使用 pflag 库来解析选项。
函数说明
viper.BindPFlags(): 自动绑定了所有命令行参数。如果只需要其中一部分,可以用viper.BingPflag()选择性绑定
举例
main.go
package main
import (
"fmt"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
func TestCmdRead() {
pflag.String("mysql.ip", "127.0.0.2", "Server running address")
pflag.Int64("mysql.port", 1002, "Server running port")
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
fmt.Printf("ip :%s , port:%s", viper.GetString("mysql.ip"), viper.GetString("mysql.port"))
}
func main() {
TestCmdRead()
}
结果:
D:\go\project\test_viper>go run main.go
ip :127.0.0.2 , port:1002
3.1.2 从环境变量获取数据
如果前面都没有获取到键值,将尝试从环境变量中读取。我们既可以一个个绑定,也可以自动全部绑定。
主要函数说明
viper.BindEnv(): 绑定指定的环境变量
viper.AutomaticEnv(): 绑定所有的环境变量
举例
main.go
package main
import (
"fmt"
"github.com/spf13/viper"
)
func TestEnv() {
// 绑定环境变量
viper.BindEnv("go.path", "GOPATH")
// 省略部分代码
fmt.Println("go path: ", viper.Get("go.path"))
}
func main() {
TestEnv()
}
结果:
D:\go\project\test_viper>go run main.go
go path: D:\go\gopath
3.1.3 从配置文件获取数据
函数说明
viper.SetConfigName 设置文件名
viper.SetConfigType 设置文件类型
viper.AddConfigPath 添加搜索路径
viper.ReadInConfig: 根据类型来读取配置文件
备注
设置文件名时不要带后缀;
搜索路径可以设置多个,viper 会根据设置顺序依次查找;
举例
config.toml
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 = 7382
main.go
package main
import (
"fmt"
"github.com/spf13/viper"
"log"
)
func TestReadConfig() {
viper.SetConfigName("config")
viper.SetConfigType("toml")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
log.Fatal("read config failed: ", err)
}
fmt.Println(viper.Get("app_name"))
fmt.Println(viper.Get("log_level"))
fmt.Println("mysql ip: ", viper.Get("mysql.ip"))
fmt.Println("mysql port: ", viper.Get("mysql.port"))
fmt.Println("mysql user: ", viper.Get("mysql.user"))
fmt.Println("mysql password: ", viper.Get("mysql.password"))
fmt.Println("mysql database: ", viper.Get("mysql.database"))
fmt.Println("redis ip: ", viper.Get("redis.ip"))
fmt.Println("redis port: ", viper.Get("redis.port"))
}
func main() {
TestReadConfig()
}
结果:
D:\go\project\test_viper>go run main.go
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: 7382
3.1.4 从io.Reader中读取数据
viper 支持从io.Reader中读取配置。这种形式很灵活,来源可以是文件,也可以是程序中生成的字符串,甚至可以从网络连接中读取的字节流。
函数说明
viper.SetConfigType 设置文件类型
viper.ReadConfig: 根据类型来读取输入的配置数据
举例
main.go
package main
import (
"bytes"
"fmt"
"github.com/spf13/viper"
"log"
)
func TestReadIo() {
viper.SetConfigType("toml")
tomlConfig := []byte(`
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
`)
err := viper.ReadConfig(bytes.NewBuffer(tomlConfig))
if err != nil {
log.Fatal("read config failed:", err)
}
fmt.Println("redis port: ", viper.GetInt("redis.port"))
}
func main() {
TestReadIo()
}
结果:
D:\go\project\test_viper>go run main.go
redis port: 7381
3.1.5 设置默认值
举例
main.go
package main
import (
"fmt"
"github.com/spf13/viper"
)
func TestDefaultSet() {
viper.SetDefault("mysql.ip", "12.0.0.1")
viper.SetDefault("mysql.port", "1001")
fmt.Println("mysql ip: ", viper.Get("mysql.ip"))
fmt.Println("mysql port: ", viper.Get("mysql.port"))
}
func main() {
TestDefaultSet()
}
结果:
D:\go\project\test_viper>go run main.go
mysql ip: 12.0.0.1
mysql port: 1001
3.2 viper 数据操作
3.2.1 读取键值
函数说明
1 Get方法
说明:Get方法返回一个interface{}的值,使用有所不便。
2 GetType方法
说明:
- Type可以为: Bool/Float64/Int/String/Time/Duration/IntSlice/StringSlice
- 如果指定的键不存在或类型不正确,GetType方法返回对应类型的零值
如果要判断某个键是否存在,使用ISSet方法 - GetStringMap 和 GetStringMapString 直接以map返回某个键下面的所有键值,前者返回map[string]interface{},后者返回map[string]string
- AllSetting 以map[string]interface{}返回所有设置
举例
config.toml
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 = 7382
[server]
protocols = ["http", "https", "port"]
ports = [10000, 10001, 10002]
timeout = 3
main.go
package main
import (
"fmt"
"github.com/spf13/viper"
"log"
)
func TestRead() {
viper.SetConfigName("config")
viper.SetConfigType("toml")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
log.Fatal("read config failed: ", err)
}
// viper.Get 测试
fmt.Println(viper.Get("app_name"))
fmt.Println(viper.Get("log_level"))
// viper.GetType 测试
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 setting:", viper.AllSettings())
}
func main() {
TestRead()
}
结果
D:\go\project\test_viper>go run main.go
awesome web
DEBUG
protocols: [http https port]
ports: [10000 10001 10002]
timeout 3ns
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:7382]
all setting: 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:7382] server:map[ports:[100
00 10001 10002] protocols:[http https port] timeout:3]]
3.2.2 Unmarshal
举例
config.toml
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 = 7382
[server]
protocols = ["http", "https", "port"]
ports = [10000, 10001, 10002]
timeout = 3
main.go
package main
import (
"fmt"
"github.com/spf13/viper"
"log"
)
type Config struct {
AppName string
LogLevel string
MySQL MySQLConfig
Redis RedisConfig
}
type MySQLConfig struct {
IP string
Port int
User string
Password string
Database string
}
type RedisConfig struct {
IP string
Port int
}
func TestUnmarshal() {
viper.SetConfigName("config")
viper.SetConfigType("toml")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
log.Fatal("read config failed", err)
}
var allInfo Config
var mysqlInfo MySQLConfig
viper.Unmarshal(&allInfo)
viper.UnmarshalKey("mysql", &mysqlInfo)
fmt.Println("mysql info")
fmt.Println(mysqlInfo)
fmt.Println("all info")
fmt.Println(allInfo)
}
func main() {
TestUnmarshal()
}
结果
D:\go\project\test_viper>go run main.go
mysql info
{127.0.0.1 3306 dj 123456 awesome}
all info
{ {127.0.0.1 3306 dj 123456 awesome} {127.0.0.1 7382}}
3.3 viper 数据保存
函数说明:
- WriteConfig:将当前的 viper 配置写到预定义路径,如果没有预定义路径,返回错误。如果有预定义路径,路径下有同名配置文件,将会覆盖当前配置文件;
- SafeWriteConfig:与上面功能一样,但是如果配置文件存在,则不覆盖;
- WriteConfigAs:保存配置到指定路径,如果文件存在,则覆盖;参数:指定路径
- SafeWriteConfigAs:与上面功能一样,但是如果配置文件存在,则不覆盖。
举例
package main
import (
"github.com/spf13/viper"
"log"
)
func TestSave() {
viper.SetConfigName("config2")
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()
// err := viper.SafeWriteConfigAs("config3.toml")
if err != nil {
log.Fatal("write config failed: ", err)
}
}
func main() {
TestSave()
}
结果
在当前路径,产生config2.toml文件, 如果文件存在则打印错误信息
3.4 其他
3.4.1 监听文件修改
viper 可以监听文件修改,热加载配置。因此不需要重启服务器,就能让配置生效。
只需要调用viper.WatchConfig,viper 会自动监听配置修改。如果有修改,重新加载的配置
举例
config.toml
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 = 7382
[server]
protocols = ["http", "https", "port"]
ports = [10000, 10001, 10002]
timeout = 3
main.go
package main
import (
"fmt"
"github.com/spf13/viper"
"log"
"time"
)
func TestListen() {
viper.SetConfigName("config")
viper.SetConfigType("toml")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
log.Fatal("read config failed: ", 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"))
}
func main() {
TestListen()
}
结果
操作: 当程序 第一次打印“redis port” 后 修改配置文件中 redis port值为 7383
D:\go\project\test_viper>go run main.go
redis port before sleep: 7382
redis port after sleep: 7383
3399

被折叠的 条评论
为什么被折叠?



