GVM 脚手架探险记03:跟着 GVM 学习 Viper
Viper 基本介绍
Viper 是 Go 语言生态中最流行的配置管理库之一,由 Go 社区知名开发者 Steve Francia 打造。它就像一个配置管理的「瑞士军刀」,能帮你轻松搞定各种复杂的配置场景。
想象一下,你的应用程序需要从多种来源读取配置:本地文件(JSON/YAML/TOML 等)、环境变量、命令行参数,甚至远程配置服务(如 etcd、Consul)。Viper 可以将这些来源的配置无缝整合,让你无需关心配置来自哪里,只需通过统一的接口获取即可。
它的核心特性包括:
- 多格式支持:JSON、YAML、TOML、HCL 等主流配置格式一网打尽
- 配置合并:轻松组合多个来源的配置,解决配置优先级问题
- 类型安全:自动进行类型转换,避免手动解析的繁琐
- 热重载:监听配置文件变化,实时更新应用配置
- 默认值设置:为配置项提供默认值,防止空指针异常
- 环境变量友好:自动读取环境变量,特别适合容器化部署
在 GVM 脚手架中,Viper 被用来管理各种运行时配置,从数据库连接信息到日志级别,都能通过它优雅地处理。
GVM 中的 Viper 代码
go
// core/viper.go
package core
import (
"flag"
"fmt"
"os"
"path/filepath"
"github.com/flipped-aurora/gin-vue-admin/server/core/internal"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/fsnotify/fsnotify"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
// Viper 配置
func Viper() *viper.Viper {
config := getConfigPath()
// 创建独立的Viper实例
v := viper.New()
v.SetConfigFile(config)
v.SetConfigType("yaml")
err := v.ReadInConfig()
if err != nil {
panic(fmt.Errorf("fatal error config file: %w", err))
}
v.WatchConfig()
v.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("config file changed:", e.Name)
if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
fmt.Println(err)
}
})
if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
panic(fmt.Errorf("fatal error unmarshal config: %w", err))
}
// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
return v
}
// getConfigPath 获取配置文件路径, 优先级: 命令行 > 环境变量 > 默认值
func getConfigPath() (config string) {
// `-c` flag parse
flag.StringVar(&config, "c", "", "choose config file.")
flag.Parse()
// 命令行参数不为空 将值赋值于config
if config != "" {
fmt.Printf("您正在使用命令行的 '-c' 参数传递的值, config 的路径为 %s\n", config)
return
}
// 判断环境变量 GVA_CONFIG
if env := os.Getenv(internal.ConfigEnv); env != "" {
config = env
fmt.Printf("您正在使用 %s 环境变量, config 的路径为 %s\n", internal.ConfigEnv, config)
return
}
// 根据 gin 模式文件名
switch gin.Mode() {
case gin.DebugMode:
config = internal.ConfigDebugFile
case gin.ReleaseMode:
config = internal.ConfigReleaseFile
case gin.TestMode:
config = internal.ConfigTestFile
}
fmt.Printf("您正在使用 gin 的 %s 模式运行, config 的路径为 %s\n", gin.Mode(), config)
_, err := os.Stat(config)
if err != nil || os.IsNotExist(err) {
config = internal.ConfigDefaultFile
fmt.Printf("配置文件路径不存在, 使用默认配置文件路径: %s\n", config)
}
return
}
此外在 GVM 中还为配置文件提供了默认值常量
go
// core/internal/config.go
package internal
const (
// ConfigDefaultFile 默认配置文件路径
ConfigDefaultFile = "config.yaml"
// ConfigDebugFile 调试配置文件路径
ConfigDebugFile = "config.debug.yaml"
// ConfigReleaseFile 发布配置文件路径
ConfigReleaseFile = "config.release.yaml"
// ConfigTestFile 测试配置文件路径
ConfigTestFile = "config.test.yaml"
)
使用技巧点
多配置文件
一般的小型项目只需要一个配置文件,可以直接代码根目录取文件就行。
当需要多配置文件时,可以直接复制拿去使用。
- 提供了动态获取配置文件的方法
getConfigPath
支持从命令行、环境变量、默认配置读取项目配置。 - 最后关于文件是否存在通过
os.Stat
方法判断。当执行文件不存在时使用默认的配置文件。
创建 Viper 实例
go
v := viper.New()
v.SetConfigFile(config)
v.SetConfigType("yaml")
通过 New() 的方式来获取 Viper 实例,可以根据需求来配置不同的实例。实例之间互不影响。
在对实例设置配置文件的名称、配置文件结尾后缀时,需要注意的是:
- 配置文件名称可以是相对路径也可以是绝对路径。
- 配置文件结尾后缀可以是
yaml
、yml
、json
、toml
等。
当你项目不需要多配置文件时,你可以这样做
go
viper.SetConfigFile("./config.yaml")
直接使用 viper
结构体,并且直接设置指定配置文件。后续操作 Viper 相关直接 viper.
即可。
当项目配置文件简单时,强烈建议你像下面方式做
go
v := viper.New()
v.SetConfigFile("./config.yaml")
其他
go
// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
这块代码是为了 GVM 的低代码平台使用。
基于执行时的工作目录,而不是代码文件位置
例如:
当你在 /home/user/projects/myproject/ 执行程序时会返回 /home/user/projects/