Go viper 快速模板


实例

此部分主要涉及对配置文件的读取,与在配置文件查询不到时的处理逻辑

// 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 ""
}

如果本文帮助到了你,帮我点个广告可以咩(o′┏▽┓`o)


评论
  目录