目录
- 一、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 上现在已有配置
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)
}
}