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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值