实例
此部分主要涉及对配置文件的读取,与在配置文件查询不到时的处理逻辑
// init.go
package conf
//go:generate go get -u github.com/spf13/viper
import (
"github.com/fsnotify/fsnotify"
"github.com/pkg/errors"
"github.com/spf13/viper"
"log"
"os"
"path"
"sync"
)
var configInstance = new(struct {
*viper.Viper
sync.Once
})
func config() *viper.Viper {
configInstance.Once.Do(
func() {
configInstance.Viper = viper.New()
log.Println("init config")
configInstance.MergeConfigMap(defaultConfig)
configDir := "./"
configName := "config"
configType := "yaml"
//设置配置文件路径
//将默认值设置到config中
configInstance.AddConfigPath(configDir)
configInstance.SetConfigName(configName)
configInstance.SetConfigType(configType)
// 配置文件出错
if err := configInstance.ReadInConfig(); err != nil {
// 如果找不到配置文件,则提醒生成配置文件并创建它
var configFileNotFoundError viper.ConfigFileNotFoundError
if errors.As(err, &configFileNotFoundError) {
// 如果 config 目录不存在,则创建它
if _, err := os.Stat(configDir); os.IsNotExist(err) {
if err = os.MkdirAll(configDir, 0755); err != nil {
log.Panic(errors.Wrapf(err, "[error] Failed to create config directory. %s\n", configDir))
}
}
configPath := path.Join(configDir, configName+"."+configType)
log.Println(errors.Wrapf(err, "[warning] Config file not found. Generating default config file at %s\n", configPath))
if err := configInstance.WriteConfigAs(configPath); err != nil {
log.Panic(errors.Wrapf(err, "[error] Failed to generate default config file. %s\n", configPath))
}
// 再次读取配置文件
if err := configInstance.ReadInConfig(); err != nil {
log.Panic(errors.Wrapf(err, "[error] Failed to read config file. %s\n", configPath))
}
panic("请修改配置文件后重启程序")
}
}
configInstance.WatchConfig()
configInstance.OnConfigChange(func(e fsnotify.Event) {
ReloadFunc()
})
})
return configInstance.Viper
}
func AddPath(path string) {
log.Printf("add path %s to config\n", path)
configInstance.Viper.AddConfigPath(path)
}
// ReloadFunc 此处是配置文件变更后的回调函数
func ReloadFunc() {
if err := configInstance.ReadInConfig(); err != nil {
log.Panic(errors.Wrap(err, "Failed to reload config"))
}
}
此部分主要展示配置文件的设置与读取(采用结构化数据)
// instances.go
package conf
import (
"github.com/pkg/errors"
"sync")
var defaultConfig = map[string]any{
"system": system{
Env: "dev",
},
}
// SystemConfig 系统配置
type system struct {
Env string
Host string
}
var systemInstance = new(struct {
sync.Once
*system
})
func System() *system {
systemInstance.Do(
func() {
err := config().Sub("system").Unmarshal(&systemInstance.system)
if err != nil {
panic(errors.New("init systemConfig...failed").Error())
}
})
return systemInstance.system
}
注意事项
同名不同类文件的识别问题
当你使用如下方式读取配置时,viper会从./conf
目录下查找任何以config
为文件名的配置文件,如果同时存在./conf/config.json
和./conf/config.yaml
两个配置文件的话,viper
会根据而ext的顺序进行文件读取
viper.SetConfigName("config")
viper.AddConfigPath("./conf")
viper.SetConfigType("yaml")
无法实现预期效果
func (v *Viper) searchInPath(in string) (filename string) {
v.logger.Debug("searching for config in path", "path", in)
for _, ext := range SupportedExts {
v.logger.Debug("checking if file exists", "file", filepath.Join(in, v.configName+"."+ext))
if b, _ := exists(v.fs, filepath.Join(in, v.configName+"."+ext)); b {
v.logger.Debug("found file", "file", filepath.Join(in, v.configName+"."+ext))
return filepath.Join(in, v.configName+"."+ext)
}
}
if v.configType != "" {
if b, _ := exists(v.fs, filepath.Join(in, v.configName)); b {
return filepath.Join(in, v.configName)
}
}
return ""
}