Viper的使用
在学习如何使用viper之前,我们需要先了解一些关于配置的相关概念。
来源
从配置的来源上进行分类,我们一般可以分为:
- 启动参数:某一次运行时需要的参数,可以考虑在这里提供。最常见的就是命令行工具的使用,会要求我们传入各种参数。
- 环境变量:和具体的实例有关的参数都放在这里。比如说,实例的权重,实例的分组信息等。
- 配置文件:一些当下环境中所需要的通用的配置。比如常用的数据库连接信息等。
- 远程配置中心:它和配置文件可以说是相互补充的,除了启动程序所需的最少配置,剩下的配置都可以放在远程配置中心。
来源的优先级:所谓的优先级,是指如果不同来源都有同一个配置项,那么究竟该用哪个值呢?
我先谈谈我常用的实践:
- 命令行最权威,因为是我主动在启动的时候输入的,所以一切配置中,它的优先级别最高。
- 环境变量次之,因为这是我自己电脑中设置的环境变量,所以它的优先级也比较高。
- 配置文件再次之,因为配置文件可能是同事写的,我只是通过Git将其同步下来而已。
- 远程配置中心优先级最低。
远程配置中心的两次加载:
- 第一次加载最基本的配置,包括:
- 远程配置的连接信息,二次加载的时候需要先连上配置中心。
- 日志相关配置,确保日志模块初始化成功,后续可以输出日志。
- 第二次则是完全加载:
- 读取系统所需的全部依赖,并且用于初始化各种第三方。例如,连接数据库。
- 如果在第一次加载中的配置,在远程配置中心里面也能找到,那么就会被覆盖掉,并且再次初始化使用这些配置的组建。比如说,再次初始化日志组件,连上日志分析平台等。
读取本地配置
安装viper
go get github.com/spf13/viper
初始化
使用viper进行初始化,有两种方式。
一种是SetConfigName
。
func InitViper() {
//读取的文件名字是 dev
viper.SetConfigName("dev")
//读取的文件类型为 yaml
viper.SetConfigType("yaml")
//当前工作目录下的 config 文件夹
viper.AddConfigPath("config")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
}
一种是使用SetConfigFile
。
func InitViperV1() {
viper.SetConfigFile("config/dev.yaml")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
}
如果这里出现了无法读取配置文件。记住,viper里面是从Go中的working directory开始定位的。
读取配置
当我们需要初始化某个功能的时候,我们就可以直接读取一个特定的配置项。
比如,我读取 redis 配置项。
func InitRedis() redis.Cmdable {
// 我们需要一个 redis 的客户端
cmd := redis.NewClient(&redis.Options{
// 使用 GetString 方法获取配置文件中的 redis 地址
Addr : viper.GetString("redis.addr"),
})
return cmd
}
配置文件中的配置。
redis:
addr: "localhost:6379"
一般,不太建议这样写。一般都是这么建议的,相关的配置都定义到一个结构体里面,然后直接初始化整个结构体就行啦。
下面是我推荐的写法。
func InitDB() *gorm.DB {
type Config struct {
DSN string `yaml:"dsn"`
}
var c Config
err := viper.UnmarshalKey("db", &c)
if err != nil {
panic("db config error")
}
db, err := gorm.Open(mysql.Open(c.DSN), &gorm.Config{})
if err != nil {
panic("db connect error")
}
return db
}
在初始化的时候,我们首先定义一个内部的结构体,用来接收全部相关的配置。(除非别的地方也要用到这个结构体,不然就保持内部定义结构体)。
设置默认值
在有时候,我们没有使用动态的配置项,而是使用了我们默认的配置。该如何实现呢?
这里有两种实现方式:
- 一种是使用viper中的
SetDefault
方法。
func InitViperV1() {
// 设置 db.dsn 默认值
viper.SetDefault("db.dsn", "root:123456@tcp(127.0.0.1:3306)/we_book?charset=utf8mb4&parseTime=True&loc=Local")
viper.SetConfigFile("config/dev.yaml")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
}
- 一种是利用好结构体,再调用
UnmarshalKey
之前,设置好默认值。如果配置里面没有对应的配置项,那么最终结果就是使用默认值。
func InitDB() *gorm.DB {
type Config struct {
DSN string `yaml:"dsn"`
}
// 在结构体中使用默认的 dsn
c := Config{
DSN: "root:123456789@tcp(127.0.0.1:3306)/we_book?charset=utf8mb4&parseTime=True&loc=Local",
}
...
}
这里,我更倾向于用第二种,因为可以把默认值放到业务对应的地方。
直接读取
如果我们想要直接读取,可以使用ReadConfig
,这个方法允许传入一个io.Reader
。
func InitViperV2() {
cfg := `
db:
dsn: "root:123456789@tcp(127.0.0.1:3306)/we_book?charset=utf8mb4&parseTime=True&loc=Local"
redis:
addr: "localhost:6379"
`
viper.SetConfigType("yaml")
err := viper.ReadConfig(bytes.NewReader([]byte(cfg)))
if err != nil {
panic("read config error")
}
}
以上就是关于viper的简单使用。