首页 > 其他分享 >Go项目学习(2)-viper

Go项目学习(2)-viper

时间:2023-09-19 21:36:31浏览次数:45  
标签:key fmt 学习 Println viper func Go string

前言

GitHub地址 Api地址

详细教程可直接参考Github,已经很详细了,这里只进行简单入门知识总结。

介绍

用来获取配置,配置可来自flag、环境变量、配置文件、远程配置……

获取配置的优先级Set,flag,env,config,key/value stroe,default。

配置项的值可以直接通过Viper中一系列get函数获取,也可以将配置解析到结构体中。

配置的key是大小写不敏感的,在获取环境变量时认定系统环境变量是大小写敏感的。

想要进阶学习,就去看一遍Api

安装

新建项目后导入依赖

go get github.com/spf13/viper

使用

配置默认值

// 声明
func SetDefault(key string, value interface{})
func main() {
	viper.SetDefault("foo", "bar")
	fmt.Println(viper.GetString("foo"))
}
# output
bar

配置文件

直接指定一个文件:

// 声明
func SetConfigFile(in string)

也可以根据文件路径查找:

// 添加文件路径
func AddConfigPath(in string)
// 设置文件名称,不包含扩展名,默认文件名称为 config
func SetConfigName(in string)
// 设置文件类型
func SetConfigType(in string)

优先级问题

  • 如果添加多个文件路径,最先添加的路径优先级越高
  • 如果既指定文件,又有配置文件路径,好像是SetConfigFile()SetConfigName()谁后执行谁优先,个人不建议混着写。

配置完后,都要读取配置文件,不然取不到值:

// 声明
func ReadInConfig() error

创建配置文件setting.yaml

a:
  b: 12

main.go

// 直接指定文件
func main() {
	viper.SetConfigFile("./setting.yaml")
	err := viper.ReadInConfig()
	if err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("config file not found")
		} else {
			panic(fmt.Errorf("failed to read config file"))
		}
	}
	fmt.Println(viper.GetInt("a.b"))
}

// 根据指定路径查找
func main() {
	viper.SetConfigName("setting")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	err := viper.ReadInConfig()
	if err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("config file not found")
		} else {
			panic(fmt.Errorf("failed to read config file"))
		}
	}
	fmt.Println(viper.GetInt("a.b"))
}
# output
12

viper可以监听配置文件的变化

func WatchConfig()

除此之外,viper还提供了从io.Reader中读取功能、配置文件change时间监听功能、写出文件功能,参考GitHub地址 Api地址

环境变量

BindEnv()方法将viper key与环境变量绑定,调用BindEnv()时不固定值,所以每次访问都是实时数据。

// 声明
func BindEnv(input ...string) error
func main() {
	os.Setenv("AA", "value a")
	os.Setenv("bb", "value b")
	// 第一个变量是viper key,后面的是环境变量,
	// 环境变量按照顺序与viper key进行匹配,直到环境变量有值
	viper.BindEnv("a", "aa", "AA", "bb")
	viper.BindEnv("b", "bb")
	fmt.Println(viper.GetString("a"))
	fmt.Println(viper.GetString("b"))
}
# output
value a
value b

AutomaticEnv()会让viper自动检查环境变量是否匹配viper的key。

func main() {
	os.Setenv("AA", "value aa")
	viper.AutomaticEnv()
	fmt.Println(viper.GetString("aa"))
}
# output
value aa

SetEnvPrefix()定义环境变量的前缀。

// 声明
func SetEnvPrefix(in string)

需要注意的是,一旦设置了前缀,viper将查找的环境变量都会是大写字母。

func main() {
	os.Setenv("V_A", "value a")
	os.Setenv("V_b", "value b")
	os.Setenv("v_c", "value c")
	viper.SetEnvPrefix("v")
	viper.AutomaticEnv()
	fmt.Println(viper.GetString("a"))
	fmt.Println(viper.GetString("b"))
	fmt.Println(viper.GetString("c"))
}
# output 只打印了环境变量为"V_A"的值
value a


更多信息参考:GitHub地址 Api地址

结合flag

viper团队自己弄了一套pflag,可以无损替换标准库的flag库,只需要在依赖声明的时候取个别名:import flag "github.com/spf13/pflag",viper之所以能结合flag,就是靠pflag。

pflag库同样用在cobra中,viper和cobra是同一个团队的,所以能很好的集成到一起:

// 官方示例
serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))

不考虑cobra的情况:

func main() {
	name := pflag.String("name", "", "usage of name")
	_ = pflag.Int("age", 18, "usage of age")
	pflag.Parse()
	fmt.Println("get from flag: ", *name)
	viper.BindPFlags(pflag.CommandLine)
	fmt.Println("get from viper: ", viper.GetString("name"))
	fmt.Println("get from viper: ", viper.GetString("age"))
}
# output
get from flag:  abc
get from viper:  abc
get from viper:  18

远程配置

初学暂时用不到,后面弄微服务再补充。Remote Key/Value Store Support

获取值

viper提供了针对不同类型,提供了一系列不同的get方法,详见Api

需要注意的是如果没有查到对应的key,viper将返回相应类型的零值,可以用IsSet()方法判断是否有对应的key:

// 声明
func IsSet(key string) bool

在json、yaml等文件中,多个层级的key,在viper中用.隔开,同java的springboot。需要注意的是,如果key的父级被其他数据源的配置覆盖了,所有的子集都会变为未定义。

viper支持使用Sub()方法取出子配置,可以作为子模块的配置传递。

// 声明
func Sub(key string) *Viper

将配置解码到结构体

viper支持将配置解析到结构体,类似java的springboot。

// 声明
func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error
func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error

这两个方法都差不多,只是后面那个多了一个指定的key。底层的字段映射是用的mapstructure库,opts也是和这个库有关,有必要的时候再研究这个吧。怎么将一个字符串分割成切片放进结构体,也是用这个库,参考这个博客,等需要的时候再研究。

需要注意的是,如果设置了监听文件的变化,结构体数据不会更新,需要在监听事件中重新解析给结构体。

# setting.yaml
user:
  name: 张三
  age: 18
aaa: aaa # 这里一开始设置的是其他值,在程序执行过程中,再修改成aaa
// main.go
type Setting struct {
	User   User
	FieldA string `mapstructure:"aaa"`
	FieldB string `mapstructure:"bbb"`
}

type User struct {
	Name string
	Age  uint
}

func main() {
	// 设置配置文件信息,监听变化,读取文件
	viper.AddConfigPath(".")
	viper.SetConfigName("setting")
	viper.WatchConfig()
	err := viper.ReadInConfig()
	if err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			fmt.Println("config file not found")
			return
		} else {
			panic(fmt.Errorf("faild to read config file: %w", err))
		}
	}

	// 这里验证了不同数据源的配置,也能解析到结构体
	viper.Set("bbb", "bbb")

	// 解析到结构体
	var s Setting
	viper.Unmarshal(&s)
	fmt.Println(s)

	// 解析到map
	var m map[string]interface{}
	viper.Unmarshal(&m)
	fmt.Println(m)

	// 当监听到配置文件有变动时,需要重新赋值给结构体
	viper.OnConfigChange(func(in fsnotify.Event) {
		fmt.Println("changed")
		viper.Unmarshal(&s)
	})

	// 验证中途修改配置能否更新到结构体
	time.Sleep(10 * time.Second)
	viper.Set("bbb", "ccc")
	fmt.Println(s)
}
# output
{{张三 18} asd bbb}
map[aaa:asd bbb:bbb user:map[age:18 name:张三]]
# 这是手动修改配置文件的值,触发了change事件
changed
{{张三 18} aaa bbb}

多个viper实例

viper是开箱即用的,不需要额外配置,但也提供了方法创建多个实例,用法都一样。

func New() *Viper

后语

整理GitHub得到的这篇笔记,后面学cobra和viper结合。

标签:key,fmt,学习,Println,viper,func,Go,string
From: https://www.cnblogs.com/xujiahao718/p/17715856.html

相关文章

  • django创建网站核心流程
       django创建网站核心步骤有7步。只有理清这7个步骤才能正确使用django创建网站。下面结合创建实例演示一下7个步骤。   第一步:打开dos窗口,在当前目录下创建工程myweb   第二步:进入myweb文件夹,创建网页项目firstapp   第三步:进入firstapp文件夹,创建模......
  • 每日学习之phoenix快速入门
    1.建表语句createtableifnotexists表名(ROWKEY名称数据类型primarykey,列簇名.列名1数据类型NOTNULL,列簇名.列名2数据类型NOTNULL,列簇名.列名3数据类型,列簇名.列名4数据类型);2.删除表droptableifexists表名;3.插入数据up......
  • MongoDB简单使用
    介绍MongoDB是一个免费的开源跨平台面向文档的NoSQL数据库。安装dockerpullmongo下载最新版本的镜像dockerrun-d--namemongo-eMONGO_INITDB_ROOT_USERNAME=root-eMONGO_INITDB_ROOT_PASSWORD=xxx123-p27017:27017mongo设置初始账号和密码,注意开启防火墙对......
  • 《信息安全系统设计与实现》第三周学习笔记
    《信息安全系统设计与实现》第三周学习笔记一门程序设计语言具有以下必备的要素和技能:语法:程序设计语言需要定义一套语法规则,以确定如何组织和编写代码。这包括变量声明、控制流语句(如条件语句和循环语句)、函数定义等。数据类型:语言需要支持不同的数据类型,如整数、浮点数、......
  • 学习java五月计划
    第一月了解java,学习Java的基础语法,了解常用关键字的使用。搭建Java开发环境,熟练使用idea开发工具进行开发。掌握运算符、表达式、流程控制语句、数组等的使用。第二月面向对象编程,要掌握Java面向对象的基本知识。掌握一些常用类string、arraylist等的使用.掌握开发中......
  • Go每日一库之15:gojsonq
    简介在日常工作中,每一名开发者,不管是前端还是后端,都经常使用JSON。JSON是一个很简单的数据交换格式。相比于XML,它灵活、轻巧、使用方便。JSON也是RESTfulAPI推荐的格式。有时,我们只想读取JSON中的某一些字段。如果自己手动解析、一层一层读取,这就变得异常繁琐了。特别是在......
  • clickhouse学习笔记
    一、query相关1.展示正在处理的请求列表showprocesslist2.杀掉正在处理的查询KILLQUERYWHEREquery_id='2-857d-4a57-9ee0-327da5d60a90'二、修改名称--1.重命名RENAMEDATABASE|TABLE|DICTIONARYnameTOnew_name--2.交换2个表的名称EXCHANGETAB......
  • Vue源码学习(七):合并生命周期(混入Vue.Mixin)
    好家伙, 1.使用场景现在来,来想一下,作为一个使用Vue的开发者,假设现在我们要使用created(),我们会如何使用1.1. .vue文件中使用<template><div><h1>{{message}}</h1></div></template><script>exportdefault{created(){this.message='......
  • openGauss学习笔记-74 openGauss 数据库管理-创建和管理视图
    openGauss学习笔记-74openGauss数据库管理-创建和管理视图74.1背景信息当用户对数据库中的一张或者多张表的某些字段的组合感兴趣,而又不想每次键入这些查询时,用户就可以定义一个视图,以便解决这个问题。视图与基本表不同,不是物理上实际存在的,是一个虚表。数据库中仅存放视图的......
  • 其他笔记需要学习
    gmockhttp://blog.divebomb.org/2011/07/my-first-c-cmake-googletest-and-googlemock/http://blog.divebomb.org/2011/07/my-first-c-cmake-googletest-and-googlemock/https://qastack.cn/programming/7208070/googletest-how-to-skip-a-testhttps://www.onitroad.com/jc/......