Go:Viper
目录:
Viper是Golang中用于配置文件的解决方案,可以处理绝大多数类型的配置和一些特性
- 设置默认值
- 从json,toml,yaml等格式获取配置信息
- 从环境变量获取
- 从远程配置程序(etcd和consul等)读取并监控配置变化
- 从命令行参数读取配置
- 实时监控和重新读取配置文件(可选功能)
参数优先级
- 显示调用set设置值
- 命令行参数
- 环境变量
- key/value存储
- 默认值
包位置
github.com/spf13/viper
功能介绍
设置默认值
viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
读取配置文件
Viper只支持单个配置文件,如果没有扩展名需要指定配置类型
viper.SetConfigName("config") // 配置文件名称(无扩展名)
viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则需要配置此项
viper.AddConfigPath("/etc/appname/") // 查找配置文件所在的路径
viper.AddConfigPath("$HOME/.appname") // 多次调用以添加多个搜索路径
viper.AddConfigPath(".") // 还可以在工作目录中查找配置
err := viper.ReadInConfig() // 查找并读取配置文件
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// 配置文件未找到错误;如果需要可以忽略
} else {
// 配置文件被找到,但产生了另外的错误
}
}
写入配置文件
支持,但是用的比较少
监控并重新读取配置文件
支持在运行过程中获取配置文件的变更,检测配置变化了就调用回调函数
确保在调用WatchConfig()之前添加了所有的配置路径。
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
// 配置文件发生变更之后会调用的回调函数
fmt.Println("Config file changed:", e.Name)
})
从内存读取配置
那我直接设置配置不好吗,或者常量,不知道为啥有这个功能
viper.SetConfigType("yaml") // 或者 viper.SetConfigType("YAML")
// 任何需要将此配置添加到程序中的方法。
var yamlExample = []byte(`
Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:
jacket: leather
trousers: denim
age: 35
eyes : brown
beard: true
`)
viper.ReadConfig(bytes.NewBuffer(yamlExample))
viper.Get("name") // 这里会得到 "steve"
覆盖设置
viper.Set("Verbose", true)
viper.Set("LogFile", LogFile)
使用环境变量
示例
package main
import (
"fmt"
"os"
"github.com/spf13/viper"
)
func main() {
viper.SetEnvPrefix("spf") // 前缀将自动转为大写
viper.BindEnv("id")
os.Setenv("SPF_ID", "13") // 设置环境变量
id := viper.Get("id") // 13
fmt.Println(id)
}
使用Flag
package main
import (
"fmt"
"flag"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
func main() {
flag.String("flagname", "why", "help message for flagname")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
flagname := viper.Get("flagname") // 13
fmt.Println(flagname)
}
数据获取方式
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]in
GetStringMapString(key string) : map[str
GetStringSlice(key string) : []string
GetTime(key string) : time.Time
GetDuration(key string) : time.Duration
IsSet(key string) : bool
AllSettings() : map[string]interface{}
访问嵌套字段
示例json配置文件
{
"host": {
"address": "localhost",
"port": 5799
},
"datastore": {
"metric": {
"host": "127.0.0.1",
"port": 3099
},
"warehouse": {
"host": "198.0.0.1",
"port": 2112
}
}
}
可以通过分隔符路径获取
viper.GetString("datastore.metric.host")
如果直接有"datastore.metric.host"字段就会覆盖该结果
{
"datastore.metric.host": "0.0.0.0",
"datastore": {
"metric": {
"host": "127.0.0.1",
"port": 3099
}
}
子树
示例配置
app:
cache1:
max-items: 100
item-size: 64
cache2:
max-items: 200
item-size: 80
通过子树的方式进行不同配置的使用
func NewCache(cfg *Viper) *Cache {...}
cfg1 := viper.Sub("app.cache1")
cache1 := NewCache(cfg1)
cfg2 := viper.Sub("app.cache2")
cache2 := NewCache(cfg2)
反序列化
type config struct {
Port int
Name string
PathMap string `mapstructure:"path_map"`
}
var C config
err := viper.Unmarshal(&C)
if err != nil {
t.Fatalf("unable to decode into struct, %v", err)
}
如果key有.
,需要重新制定分隔符
v := viper.NewWithOptions(viper.KeyDelimiter("::"))
v.SetDefault("chart::values", map[string]interface{}{
"ingress": map[string]interface{}{
"annotations": map[string]interface{}{
"traefik.frontend.rule.type": "PathPrefix",
"traefik.ingress.kubernetes.io/ssl-redirect": "true",
},
},
})
type config struct {
Chart struct{
Values map[string]interface{}
}
}
var C config
v.Unmarshal(&C)
还支持嵌套
type config struct {
Module struct {
Enabled bool
moduleConfig `mapstructure:",squash"`
}
}
// moduleConfig could be in a module specific package
type moduleConfig struct {
Token string
}
var C config
err := viper.Unmarshal(&C)
if err != nil {
t.Fatalf("unable to decode into struct, %v", err)
}
示例
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigName("config") // 指定配置文件名称(不需要带后缀)
viper.SetConfigType("yaml") // 指定配置文件类型
viper.AddConfigPath("./conf/") // 指定查找配置文件的路径(这里使用相对路径)
err := viper.ReadInConfig() // 读取配置信息
if err != nil { // 读取配置信息失败
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
r := gin.Default()
if err := r.Run(fmt.Sprintf(":%d", viper.Get("port"))); err != nil {
panic(err)
}
}
配置文件为./conf/config.yaml
port: 4399