浅谈Golang如何使用Viper进行配置管理

 更新时间:2023年06月27日 11:44:03   作者:Shawn27  
在Golang生态中,Viper是一个不错的开源配置管理框架,这篇文章主要为大家介绍了Golang如何使用Viper进行配置管理,需要的可以参考一下

前言

应用程序里要用到很多配置,比如监听端口、数据库相关配置、缓存相关配置等。这些配置可能来自不同的源,最常见的是本地配置文件,也可以作为命令行参数或环境变量传入,还可以托管在远程配置中心。同一项配置可能在多个不同的配置源中同时存在,如何处理优先级?在程序运行过程中,有些配置需要能够动态更新,又该如何实现?配置的源、优先级及动态更新,就是配置管理的内容。

在Golang生态中,Viper是一个不错的开源配置管理框架。

配置层级

Viper支持多种配置源,并处理了它们之间的优先级问题,按优先级从高到低的顺序,层级如下:

  • 命令行参数
  • 环境变量
  • 本地配置文件
  • 远程配置文件
  • 默认值

下面分别看一下Viper是如何支持这些配置源的。

0. 配置结构

以如下的yaml格式配置文件来说明:

# myconfig.yaml
aaa: foo
xxx:
    bbb: bar
    ccc: 100

Viper在内存中是用map类型来维护配置的,以配置名作为key、配置值作为Value。

为方便使用,我们可以定义如下的结构体来接收配置:

package config
var Config config
type config struct {
    Aaa string
    Xxx struct {
        Bbb string
        Ccc uint
    }
}

1. 命令行参数

Viper提供了BindPFlags函数来绑定命令行参数,以Cobra命令行框架为例,初始化代码如下:

// 1. 根据需要定义命令行参数
pflags = rootCmd.PersistentFlags()
pflags.StringP("aaa", "a", "", "aaa's usage")
pflags.String("xxx.bbb", "", "xxx.bbb's usage")
pflags.Uint("xxx.ccc", 0, "xxx.ccc's usage")
// 2. Viper绑定命令行参数
viper.BindPFlags(pflags)

这样就可以通过命令行参数来传入相关配置:

// 参数aaa使用长参数格式
$ mycmd --aaa foo --xxx.bbb bar --xxx.ccc 100
// 参数aaa使用短参数格式
$ mycmd -a foo --xxx.bbb bar --xxx.ccc 100

2. 环境变量

Viper提供了AutomaticEnv函数来从环境变量中加载配置,示例代码如下:

// 设置环境变量的前缀
viper.SetEnvPrefix("MYCMD")
// 将配置Key中的点号(.)和横杠(-)替换为下划线(_)
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
// 从环境变量中读取匹配的配置
viper.AutomaticEnv()

这样就可以通过环境变量来传入相关配置:

$ MYCMD_AAA=foo MYCMD_XXX_BBB=bar MYCMD_XXX_CCC=100 mycmd

3. 本地配置文件

Viper提供了从本地配置文件加载配置的方法,示例代码如下:

// 在初始化cobra时,将本地配置文件路径定义为命令行参数
pflags.StringVarP(&cfgFile, "config", "c", "", "config file")
// 在初始化viper时,设置本地配置文件路径
viper.SetConfigFile(cfgFile)
// 读取本地配置文件
if err := viper.ReadInConfig(); err != nil {
    log.Fatal(err)
}

这样就可以通过本地配置文件来传入相关配置:

// myconf.yaml为同目录下的配置文件,内容如上文所述
$ mycmd --config myconf.yaml

4. 远程配置文件

Viper还支持将配置文件托管在远程配置中心,如etcd,示例代码如下:

// 注意:在实际项目中,以下参数也可以作为配置传入
provider := "etcd3"
endpoint := "x.x.x.x:2379"
path := "/config/mycmd/myconf.yaml"
viper.AddRemoteProvider(provider, endpoint, path)
viper.SetConfigType("yaml")
if err := viper.ReadRemoteConfig(); err != nil {
    log.Println("Read remote config failed:", err)
}

只要将myconf.yaml配置文件上传到etcd的指定路径下,程序就可以远程读取了。

$ cat myconf.yaml | etcdctl put /config/mycmd/myconf.yaml

5. 默认值

Viper还支持设置默认值,对于在以上所有源中都不存在的配置,将使用其默认值,示例代码如下:

viper.SetDefault("aaa", "foo")
viper.SetDefault("xxx.bbb", "bar")
viper.SetDefault("xxx.ccc", 100)

在所有配置源都加载后,我们可以将Viper配置装载到配置结构体变量中,方便使用:

if err := viper.Unmarshal(&config.Config); err != nil {
    log.Fatal("Viper unmarshal failed:", err)
}

配置动态更新

以上解决了静态配置管理的问题,如何实现配置的动态更新呢?

配置的动态更新包括三个步骤:

  • 更新配置源中的配置
  • 更新内存中的配置
  • 配置的动态生效

注意:如Viper官方文档所提,Viper本身不是并发安全的,在实现配置动态更新时,要注意采用锁机制等方式来保证Viper并发读写的安全。

1. 更新配置源中的配置

在Viper支持的配置源中,命令行参数、环境变量是在进程启动时一次性读取的,不支持动态更新。本地配置文件和远程配置文件可以支持动态更新,直接修改配置文件即可。

2. 更新内存中的配置

Viper支持监听本地配置文件的变化,同时允许注册文件变化时的回调函数,借助这个功能可以实现本地配置文件的动态更新,示意代码如下:

viper.OnConfigChange(func(e fsnotify.Event) {
    // 回调函数
    // 可在此处更新内存中的本地配置:
    // 1. 重新读取本地配置:ReadInConfig
    // 2. 装载到配置结构体:Unmarshal
    // 注意:为Viper及配置结构体加锁
})
viper.WatchConfig()

对于远程配置文件,可以通过周期性地重新读取来实现动态更新,示意代码如下:

go func() {
    for {
        time.Sleep(time.Second * 10)
        // 周期为10s
        // 可在此处更新内存中的远程配置:
        // 1. 重新读取远程配置:WatchRemoteConfig
        // 2. 装载到配置结构体:Unmarshal
        // 注意:为Viper及配置结构体加锁
    }
}()

3. 配置的动态生效

不同配置的使用方式不同,动态生效方式也就不同,大概可以分为两类:

  • 有些配置,每次使用时都是读取最新的值,因此只要更新了内存中的配置,就会即时生效
  • 有些配置,比如数据库凭据,可能需要重建连接池才能生效

在这里就不再细说了。

总结

本文介绍了在Golang中优雅、灵活地实现配置管理的一种方式,涵盖配置的源、优先级及动态更新等内容。重点说明了命令行参数、环境变量、本地配置文件、远程配置文件等多种配置源的实现方法,以及配置动态更新的实现思路。

以上就是浅谈Golang如何使用Viper进行配置管理的详细内容,更多关于Go Viper配置管理的资料请关注脚本之家其它相关文章!

相关文章

  • golang panic及处理机制

    golang panic及处理机制

    Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱,今天给大家介绍golang panic及处理机制,需要的朋友参考下吧
    2021-08-08
  • golang如何用http.NewRequest创建get和post请求

    golang如何用http.NewRequest创建get和post请求

    这篇文章主要介绍了golang如何用http.NewRequest创建get和post请求问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • golang高性能的http请求 fasthttp详解

    golang高性能的http请求 fasthttp详解

    fasthttp 是 Go 的快速 HTTP 实现,当前在 1M 并发的生产环境使用非常成功,可以从单个服务器进行 100K qps 的持续连接,总而言之,fasthttp 比 net/http 快 10 倍,下面通过本文给大家介绍golang fasthttp http请求的相关知识,一起看看吧
    2021-09-09
  • golang端口占用检测的使用

    golang端口占用检测的使用

    这篇文章主要介绍了golang端口占用检测的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Go每日一库之zap日志库的安装使用指南

    Go每日一库之zap日志库的安装使用指南

    这篇文章主要为大家介绍了Go每日一库之zap安装使用示例学习,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Golang反射获取结构体的值和修改值的代码示例

    Golang反射获取结构体的值和修改值的代码示例

    这篇文章主要给大家介绍了golang反射获取结构体的值和修改值的代码示例及演示效果,对我们的学习或工作有一定的帮助,感兴趣的同学可以参考阅读本文
    2023-08-08
  • golang实现整型和字节数组之间的转换操作

    golang实现整型和字节数组之间的转换操作

    这篇文章主要介绍了golang实现整型和字节数组之间的转换操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • golang channel多协程通信常用方法底层原理全面解析

    golang channel多协程通信常用方法底层原理全面解析

    channel 是 goroutine 与 goroutine 之间通信的重要桥梁,借助 channel,我们能很轻易的写出一个多协程通信程序,今天,我们就来看看这个 channel 的常用用法以及底层原理
    2023-09-09
  • Go异步任务解决方案之Asynq库详解

    Go异步任务解决方案之Asynq库详解

    需要在Go应用程序中异步处理任务? Asynq,简单高效的任务队列实现,下面这篇文章主要给大家介绍了关于Go异步任务解决方案之Asynq库的相关资料,需要的朋友可以参考下
    2023-02-02
  • Go语言调用其它程序并获得程序输出的方法

    Go语言调用其它程序并获得程序输出的方法

    这篇文章主要介绍了Go语言调用其它程序并获得程序输出的方法,实例分析了Go调用cmd程序的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02

最新评论