当前位置: 首页 > news >正文

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

目录

  • 一、viper简介
  • 二、基本使用
    • 1.viper基本配置
    • 2.读取配置
    • 3.自定义配置源
    • 4.注册别名
    • 4.获取环境变量
    • 5.获取命令行参数
    • 6.多实例使用
    • 7.监听配置变化
  • 三、读取远程配置
  • 四、保存配置

一、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)
	}
}

相关文章:

  • 170-本地WIFI测试环境配置IP
  • 【面试宝典】Mysql面试题大全
  • Unity 符号表
  • Sentinel--服务容错
  • rust编译器教我做人,为啥还要学习rust语言,因为想使用rust做一些底层服务,更深入的研究技术。
  • Visual Assist v10.9.2471.0 Crack
  • 面试题:SpringBoot调用http服务几种方式
  • yolov7配置与训练记录(二)
  • Spring Cloud(十五):微服务自动化部署 DevOps CI/CD、Maven打包、ELK日志采集
  • [设计] Doris血缘解析流程
  • 【校招VIP】[推电影项目]商业项目的竞品分析和需求分析
  • 语法练习:array123
  • 设计模式之原型模式
  • JVM之垃圾收集器三
  • 【web前端期末大作业】基于html关爱空巢老人网页设计与实现
  • flutter课程(The Complete 2021 Flutter Development Bootcamp with Dart)学习总结
  • 架构师知识体系梳理
  • LaTex使用技巧9:argmin / argmax下标写法
  • MySQL表的操作
  • 给定一个已排序的数组,使用就地算法将重复的数字移除,使数组中的每个元素只出现一次,返回新数组的长度
  • 电加热油锅炉工作原理_电加热导油
  • 大型电蒸汽锅炉_工业电阻炉
  • 燃气蒸汽锅炉的分类_大连生物质蒸汽锅炉
  • 天津市维修锅炉_锅炉汽化处理方法
  • 蒸汽汽锅炉厂家_延安锅炉厂家
  • 山西热水锅炉厂家_酒店热水 锅炉
  • 蒸汽锅炉生产厂家_燃油蒸汽发生器
  • 燃煤锅炉烧热水_张家口 淘汰取缔燃煤锅炉
  • 生物质锅炉_炉
  • 锅炉天然气_天燃气热风炉