viper 是一个读取配置文件的库,支持多种格式,方便快捷,把文件读成可以解析的map方式.
一般把json,yaml转成go的结构体进行使用.
OnConfigChange这个函数是监听文件已经被修改了,就会进行执行这个函数.而作者的实现方式,可能是监听这个文件写入状态吧,每保存一次,就会调用一次这个函数.和我们的想法是不同的.
而且在别的文本编辑器中进行保存,可能会调用多次,我修改了一次,而执行了多次这个函数,这是不能够忍受的.
什么是配置文件修改? 最恰当的是修改了配置文件的元素.目前库是不支持这种的.而我们也没有必要对其要求过高.
如果一个文件没有发生改变,只是保存,你执行了这个函数是不对的.而解决方案之一是把配置文件进行md5一下,保存其md5值,在调用这个函数时,再次读取文件进行md5.两者比对,如果相等就不执行下面的逻辑.
这样就解决了,一模一样的配置文件,保存多次,不会执行后续的逻辑.
但是呢,杜绝了大部分的场景,比如修改了一次,保存了两次,依旧会有看起来一模一样的配置,md5值不同.但也无所谓.咱们可以如下面的说明,对每个配置进行比对.
如果仅仅是换行,或者修改了部分的配置,大家可以对其结构体转成json,再md5值,改变前和改变后的.如果值不同,就执行后续逻辑.道理是相通的.
部分参考代码
yaml
server:
port: '36501'
heartbeat: 12 #
package main
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"time"
"github.com/fsnotify/fsnotify"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
)
var confMD5 string
func GetMD5(s []byte) string {
m := md5.New()
m.Write([]byte(s))
return hex.EncodeToString(m.Sum(nil))
}
func ReadFileMd5(sfile string) (string, error) {
ssconfig, err := ioutil.ReadFile(sfile )
if err != nil {
return "", err
}
return GetMD5(ssconfig), nil
}
type ConfigServer struct {
Server Server `mapstructure:"server" json:"server"`
}
func (c *ConfigServer) MD5String() string {
jsonBytes, err := json.Marshal(c)
if err != nil {
return ""
}
return ulits.GetMD5(jsonBytes)
}
func
readcfg(){
var err error
confMD5, err = ReadFileMd5("config.yaml")
fmt.Printf("[%+v]\n", confMD5)
v := viper.New()
v.SetConfigFile("config.yaml")
err = v.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
v.WatchConfig()
allvalue := v.AllSettings()
fmt.Printf("\n\nkey [%+v]\n", allvalue)// 是所有的值
v.OnConfigChange(func(e fsnotify.Event) {
tconfMD5, _ := ReadFileMd5("config.yaml")
fmt.Printf("[%+v]\n", tconfMD5)
if tconfMD5 == confMD5 {
return
}
// 这说明文件发生了改变.
confMD5 = tconfMD5
fmt.Println("cfg start....", e.String(), e.Name)
// 定义一个接收的结构体
var ttmpcfg config.ConfigServer
// 对其结构体md5 ,这是默认的空的结构体md5值
nilMD5 := ttmpcfg.MD5String()
// 然后把配置读取到结构体当中
if err := v.Unmarshal(&ttmpcfg); err != nil {
return
}
// 注意判断当前的结构体是空的,因为程序有可能会走多次.没有读取出内容来是不对的,所以需要与空结构体json md5
if nilMD5 == ttmpcfg.MD5String() {
return
}
}
func main() {
readcfg()
}