【viper】go 配置管理神器viper使用详解 - Go语言中文社区

【viper】go 配置管理神器viper使用详解


一、viper简介

viper是一个应用程序解决方案,它支持在应用程序内使用,特性如下:

  • 支持json、toml、yaml、yml、properties、props、prop、hcl、tfvars、dotenv、env、ini 等格式的配置文件;
  • 支持从文件、环境变量、命令行、io.Reader、远程配置中心(etcd、etcd3、consul、firestore) 读取和修改配置;
  • 可以监听配置文件修改,并应用到程序中。

二、基本使用

1.viper基本配置

  • func (v *Viper) SetConfigType(in string) // 设置配置文件格式
  • func (v *Viper) SetConfigName(in string) // 设置配置文件名
  • func (v *Viper) AddConfigPath(in string) // 添加配置文件寻找路径
  • func (v *Viper) ReadInConfig() // 读取配置
func main(){
	viper.SetConfigType("yaml")        
	viper.SetConfigName("config.yaml") 
	viper.AddConfigPath(".")           
	// 读取配置
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("file not found.")
			return
		}
		panic(err)
	}
}

2.读取配置

viper 支持读取不同类型的数据。

  • func (v *Viper)GetString(key string) string
  • func (v *Viper)GetBool(key string) bool
  • func (v *Viper)GetInt(key string) int
  • func (v *Viper)GetInt32(key string) int32
  • func (v *Viper)GetInt64(key string) int64
  • func (v *Viper)GetUint(key string) uint
  • func (v *Viper)GetUint16(key string) uint16
  • func (v *Viper)GetUint32(key string) uint32
  • func (v *Viper)GetUint64(key string) uint64
  • func (v *Viper)GetFloat64(key string) float64
  • func (v *Viper)GetTime(key string) time.Time
  • func (v *Viper)GetDuration(key string) time.Duration
  • func (v *Viper)GetIntSlice(key string) []int
  • func (v *Viper)GetStringSlice(key string) []string
  • func (v *Viper)GetStringMap(key string) map[string]interface{}
  • func (v *Viper)GetStringMapString(key string) map[string]string
  • func (v *Viper)GetStringMapStringSlice(key string) map[string][]string
  • func (v *Viper)AllSettings() map[string]interface{} // 获取所有的配置项
  • func (v *Viper)IsSet(key string) bool //配置是否存在
    例如我有个配置文件为 config.yaml
# file : config.yaml
type: yaml
a1:
    k1: v1
    k2: v2
array:
    - 1
    - 2
    - 3
a1.k1: vv1

代码为:

func main(){
	viper.SetConfigType("yaml")
	viper.SetConfigName("config.yaml")
	viper.AddConfigPath(".")
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("file not found.")
			return
		}
		panic(err)
	}
	fmt.Println("type:", viper.GetString("type"))         // 读取字符串 type
	fmt.Println("a1:", viper.GetStringMap("a1"))          // 读取map a1
	fmt.Println("a1.k1:", viper.GetStringMap("a1")["k1"]) // 读取字符串a1.k1
	fmt.Println("a1.k1:", viper.GetString("a1.k1"))       // 嵌套读取字符串 a1.k1 注意和上面的区分,优先读取配置中和key完整匹配的部分
	fmt.Println("array:", viper.GetIntSlice("array"))     // 读取数组 array
}	

执行结果:
在这里插入图片描述
注意:获取配置的的方法,在key不存在的情况下,获取到的是0或者空值,可以先用 IsSet判断,例如:

if viper.IsSet("key") {
	fmt.Println("get key:", viper.GetString("key"))
}

3.自定义配置源

  • func (v *Viper) ReadConfig(in io.Reader) // 使用 io.Reader 作为配置输入源
func main(){
	yamlConfig := `
type: yaml
a1:
    k1: v1
    k2: v2
array:
    - 1
    - 2
    - 3
a1.k1: vv1
`
	viper.SetConfigType("yaml")
	
	if err := viper.ReadConfig(strings.NewReader(yamlConfig)); err != nil {
		panic(err)
	}
	fmt.Println(viper.AllSettings())
}	

4.注册别名

  • func (v *Viper)RegisterAlias(alias string, key string) // 注册别名
func main(){
	viper.SetConfigType("yaml")        // 设置配置文件格式
	viper.SetConfigName("config.yaml") // 设置配置文件名
	viper.AddConfigPath(".")           // 添加配置文件寻找路径
	// 读取配置
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("file not found.")
			return
		}
		panic(err)
	}

	viper.RegisterAlias("t", "type")
	fmt.Println(viper.GetString("t"))
}

这里可以看到,给 type 注册别名为 t,直接获取 t 就是 type 的配置。

4.获取环境变量

  • func (v *Viper)AutomaticEnv() // 自动绑定所有环境变量
  • func (v *Viper)AllowEmptyEnv(allowEmptyEnv bool) // 设置是否允许空环境变量
  • func (v *Viper)SetEnvPrefix(in string) // 设置前缀,用'_'和key连接
func main(){
	viper.AutomaticEnv()
	viper.AllowEmptyEnv(true)
	viper.RegisterAlias("env", "os_env")
	viper.SetEnvPrefix("viper")
	if err := os.Setenv("viper_os_env", "viper_alias_prefix_ok"); err != nil {
		panic(err)
	}
	fmt.Println(viper.GetString("env")) // 输出 viper_alias_prefix_ok
}

例子里可以看到,先给os_env命名别名为env,然后再加上前缀viper,所以最后获取环境变量传值为env时,实际获取的是viper_os_env

5.获取命令行参数

  • func (v *Viper)BindPFlag(key string, flag *pflag.Flag) error
  • func (v *Viper)BindPFlags(flags *pflag.FlagSet) error
    这里用到了 spf13 的 pflag 包,路径: github.com/spf13/pflag
func main(){
	pflag.IntP("int", "i", 1, "test int flag")
	pflag.StringP("string", "s", "s", "test string flag")
	pflag.Parse()

	e := viper.BindPFlag("string", pflag.Lookup("string"))
	if e != nil {
		panic(e)
	}
	fmt.Println(viper.GetString("string"))

	e = viper.BindPFlags(pflag.CommandLine)
	if e != nil {
		panic(e)
	}
	fmt.Println(viper.AllSettings())
}
// ./main.exe -t 2 -s ss
/* 结果
ss
map[int:2 string:ss]
*/

这里也可以使用大神的另一个包 cobra : github.com/spf13/cobra【后面补充个cobra的使用说明】

// 在init中绑定参数
func init() {
	cobra.OnInitialize(initCfg)
	rootCmd.PersistentFlags().StringP("version", "v", "6", "app version")
	viper.BindPFlag("version", rootCmd.PersistentFlags().Lookup("version"))
}

6.多实例使用

创建新的viper实例可以使用以下两种方法

  • func New() *Viper
  • func NewWithOptions(opts ...Option) *Viper // 根据配置创建实例
    其中第二种,viper 提供现有的配置有:
  • func KeyDelimiter(d string) Option // 设置配置项key的分隔符,默认是 '.' ,比如前面示例中的a1.k1
  • func EnvKeyReplacer(r StringReplacer) Option // 环境变量key的替换处理,如果使用Get方法获取环境变量时需要对key字符串进行某些替换动作可以传入处理接口
  • func IniLoadOptions(in ini.LoadOptions) Option // init类型配置文件加载选项
func testJson() {
	jsonViper := viper.New()
	jsonViper.SetConfigType("json")
	jsonViper.SetConfigName("config.json")
	jsonViper.AddConfigPath(".")
	if err := jsonViper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("file not found.")
			return
		}
		panic(err)
	}

	fmt.Println(jsonViper.AllSettings())
}

func testYaml() {
	yamlViper := viper.New()
	yamlViper.SetConfigType("yaml")       
	yamlViper.SetConfigName("config.yaml") 
	yamlViper.AddConfigPath(".")       

	if err := yamlViper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("file not found.")
			return
		}
		panic(err)
	}
	fmt.Println(yamlViper.AllSettings())
}


func main() {
	testJson()
	testYaml()
}

运行结果:
在这里插入图片描述

7.监听配置变化

  • func (v *Viper)OnConfigChange(run func(in fsnotify.Event)) // 注册配置变化处理函数
  • func (v *Viper)WatchConfig() // 开始监听配置变化
func main(){
	viper.SetConfigType("yaml")        // 设置配置文件格式
	viper.SetConfigName("config.yaml") // 设置配置文件名
	viper.AddConfigPath(".")           // 添加配置文件寻找路径
	// 读取配置
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("file not found.")
			return
		}
		panic(err)
	}
	viper.OnConfigChange(func(in fsnotify.Event) {
		fmt.Println("new type:", viper.GetString("type"))
	})
	viper.WatchConfig()
	time.Sleep(time.Minute)
}

三、读取远程配置

  • func (v *Viper) AddRemoteProvider(provider, endpoint, path string) // 新增远程配置
  • func (v *Viper) ReadRemoteConfig() error // 读取远程配置
  • func (v *Viper) WatchRemoteConfig() error // 监听远程配置,其实这个也是调用读取配置的接口,不是实际意义上的监听,需要循环调用
  • func (v *Viper) WatchRemoteConfigOnChannel() error // 通过 chan 监听配置,这个方法可以实时根据远程配置的变化修改本地数据,只需调用1次

这里以 etcd3 为例,不了解 etcd3 的同学可以看下我之前的博客

etcd3 上现在已有配置
![在这里插入图片描述](https://img-blog.csdnimg.cn/3f8f142226cd45d8b4f190d5a67e1780.png

func main(){
	v := viper.New()
	v.SetConfigType("json")
	// 添加 etc3 远程配置中心
	err := v.AddRemoteProvider("etcd3", "http://10.1.30.79:12379", "/config/config.json")
	if err != nil {
		panic(err)
	}
	conf := &struct {
		Type string `json:"type"`
		A1   struct {
			K1 string `json:"k1"`
			K2 string `json:"k2"`
		} `json:"a1"`
		Array []int `json:"array"`
	}{}
	// 读取远程配置
	if err = v.ReadRemoteConfig(); err != nil {
		panic(err)
	}
	// 解析成json
	if err = v.Unmarshal(conf); err != nil {
		panic(err)
	}
	fmt.Println("conf:", conf)
	
	// 监听配置改变
	if err = v.WatchRemoteConfigOnChannel(); err != nil {
		panic(err)
	}
	
	// 循环打印所有配置项
	for {
		fmt.Println(v.AllSettings())
		time.Sleep(time.Second)
	}
}

四、保存配置

  • func (v *Viper) Set(key string, value interface{}) // 给配置项设置值
  • func (v *Viper) WriteConfig() error // 保存配置到文件中
  • func (v *Viper) SafeWriteConfig() error // 安全的保存配置到文件中,若文件已存在则报错
  • func (v *Viper) WriteConfigAs(filename string) error // 配置另存为文件
  • func (v *Viper) SafeWriteConfigAs(filename string) error // 安全的另存为配置文件,若文件已存在则报错
func main(){
	viper.SetConfigType("yaml")   // 设置配置文件格式
	viper.SetConfigName("config") // 设置配置文件名
	viper.AddConfigPath(".")      // 添加配置文件寻找路径
	// 读取配置
	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("file not found.")
			return
		}
		panic(err)
	}
	
	viper.Set("type", "yml")
	if err := viper.SafeWriteConfig(); err != nil {
		panic(err)
	}
}
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_41630102/article/details/128115040
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2023-01-03 12:12:25
  • 阅读 ( 217 )
  • 分类:研发管理

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢