文章目录
- 什么是 Viper?
- 基础配置
- 引入依赖:
- 动态监听原理分析:
- 监听原理分析
- Config.yaml文件配置
- Viper文件配置
什么是 Viper?
介绍:用于处理配置文件中解析和读取配置文件
优点:支持多种配置格式,json,yaml 等等
作用:配置文件解析和读取 默认值支持 环境变量支持 命令行标志支持 配置监听和热加载
基础配置
引入依赖:
go get github.com/spf13/viper
读取文件方法:
viper.SetConfigFile("haha") //配置文件名,不需要后缀viper.SetConfigType("yaml") //设置配置文件格式viper.AddConfigPath("../config") //查找路径err := viper.ReadInConfig() //读取配置文件
读取文件方法二:
viper.SetConfigFile("./config/config.yaml")err := viper.ReadInConfig()
动态监听原理分析:
//监听配置文件
viper.WatchConfig()
//监听是否更改配置文件
viper.OnConfigChange(func(e fsnotify.Event) {fmt.Println("配置文件被人修改了...")err := viper.Unmarshal(&Conf)if err != nil {panic(fmt.Errorf("配置文件修改以后,报错啦,err:%v", err))}
})
监听原理分析
分析WatchConfig()方法
// 监听文件变化
func (v *Viper) WatchConfig() {//开启一个协程 相当于开启一个任务initWG := sync.WaitGroup{}initWG.Add(1)go func() {//创建一个文件监听器watcher, err := newWatcher()if err != nil {v.logger.Error(fmt.Sprintf("failed to create watcher: %s", err))os.Exit(1)}defer watcher.Close()// we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way//获得配置文件名filename, err := v.getConfigFile()if err != nil {v.logger.Error(fmt.Sprintf("get config file: %s", err))initWG.Done()return}//规范化配置文件路径信息configFile := filepath.Clean(filename)configDir, _ := filepath.Split(configFile)//获得文件的真实路径realConfigFile, _ := filepath.EvalSymlinks(filename)//再开启一个协程 去监听监听器的状态eventsWG := sync.WaitGroup{}eventsWG.Add(1)go func() {for {select {//监听监听器的事务case event, ok := <-watcher.Events:if !ok { // 'Events' channel is closedeventsWG.Done()return}currentConfigFile, _ := filepath.EvalSymlinks(filename)// we only care about the config file with the following cases:// 1 - if the config file was modified or created// 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)//判断文件是否修改,创建 配置文件的真实路径是否发生变化if (filepath.Clean(event.Name) == configFile &&(event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) ||(currentConfigFile != "" && currentConfigFile != realConfigFile) {realConfigFile = currentConfigFile//变化之后 重新进行文件读取err := v.ReadInConfig()if err != nil {v.logger.Error(fmt.Sprintf("read config file: %s", err))}//调用回调函数去重新将内容写进结构体中if v.onConfigChange != nil {v.onConfigChange(event)}} else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) {eventsWG.Done()return}case err, ok := <-watcher.Errors:if ok { // 'Errors' channel is not closedv.logger.Error(fmt.Sprintf("watcher error: %s", err))}eventsWG.Done()return}}}()//如果发生文件找不到或者监听过程中出错就会退出内外两层协程,然后监听停止watcher.Add(configDir)initWG.Done() // done initializing the watch in this go routine, so the parent routine can move on...eventsWG.Wait() // now, wait for event loop to end in this go-routine...}()initWG.Wait() // make sure that the go routine above fully ended before returning
}
同时通过mapstructure将配置文件中的信息(键值对)映射到结构体中,实现随时拿取
Config.yaml文件配置
mode: "dev"
port: 8080log:level: "debug"filename: "./log/logfile.log"max_size: 1000max_age: 3600max_backups: 5mysql:host: localhostport: 3306user: rootpassword: rootdb: librarymax_open_conns: 100max_idle_conns: 20redis:host: 127.0.0.1port: 6379db: 0
Viper文件配置
package configimport ("fmt""github.com/fsnotify/fsnotify""github.com/spf13/viper"
)var Conf = new(LibraryConfig)type MysqlConfig struct {Host string `mapstructure:"host"`User string `mapstructure:"user"`Password string `mapstructure:"password"`DB string `mapstructure:"db"`Port int `mapstructure:"port"`MaxOpenConns int `mapstructure:"max_open_conns"`MaxIdleConns int `mapstructure:"max_idle_conns"`
}
type RedisConfig struct {Host string `mapstructure:"host"`Port int `mapstructure:"port"`DB int `mapstructure:"db"`Password string `mapstructure:"password"`PollSize int `mapstructure:"PollSize"`MinIdleConns int `mapstructure:"min_idle_cons"`
}
type LogConfig struct {Level string `mapstructure:"level"`FileName string `mapstructure:"filename"`MaxSize int `mapstructure:"max_size"`MaxAge int `mapstructure:"max_age"`MaxBackUps int `mapstructure:"max_backups"`
}type LibraryConfig struct {Mode string `mapstructure:"mode"`Port int `mapstructure:"port"`*LogConfig `mapstructure:"log"`*MysqlConfig `mapstructure:"mysql"`*RedisConfig `mapstructure:"redis"`
}func Init() error {//加载配置文件位置viper.SetConfigFile("./config/config.yaml")//监听配置文件viper.WatchConfig()//监听是否更改配置文件viper.OnConfigChange(func(e fsnotify.Event) {fmt.Println("配置文件被人修改了...")err := viper.Unmarshal(&Conf)if err != nil {panic(fmt.Errorf("配置文件修改以后,报错啦,err:%v", err))}})// 读取配置文件内容err := viper.ReadInConfig()if err != nil {panic(fmt.Errorf("ReadInConfig failed,err:%v", err))}//将配置文件内容写入到Conf结构体if err1 := viper.Unmarshal(&Conf); err1 != nil {panic(fmt.Errorf("unmarshal data to Conf failed,err:%v", err))}return nil
}