配置及代码文件
{ "name":"sasuke", "age":25, "gender":"male", "score":99.5 }develop.json
package main import ( "crypto/md5" "encoding/hex" "encoding/json" "fmt" "io" "os" "sync" "sync/atomic" "time" ) type Student struct { Name string `json:"name"` Age int `json:"age"` Gender string `json:"gender"` Score float32 `json:"score"` } // 声明一个全局的变量,存放上一次文件变更时文件的hash值 var lastFileHash string = "" // 生命一个全局的结构体对象配置,项目中使用这个全局变量,在业务中使用 var StudentConfig *Student // 获取配置的值 func loadConfig(filePath string) (*Student, error) { file, errFile := os.Open(filePath) if errFile != nil { return nil, errFile } defer file.Close() //NewDecoder创建一个从file读取并解码json对象的*Decoder,解码器有自己的缓冲,并可能超前读取部分json数据。 decoder := json.NewDecoder(file) conf := Student{} //Decode从输入流读取下一个json编码值并保存在v指向的值里 errDecoder := decoder.Decode(&conf) if errDecoder != nil { return nil, errDecoder } return &conf, nil } // 获取文件的hash值 func getFileHash(filePath string) (string, error) { file, err := os.Open(filePath) if err != nil { return "", err } defer file.Close() m := md5.New() _, err = io.Copy(m, file) if err != nil { return "", err } return hex.EncodeToString(m.Sum(nil)), nil } // 每隔5秒校验一下配置文件有没有更新,更新的话就给监听的协裎发通知 func checkFileChanged(configValue *atomic.Value, cond *sync.Cond, filePath string) { for { time.Sleep(time.Second * 5) // 如果文件变更了,那么它的hash值会改变 fileHash, errFileHash := getFileHash(filePath) if errFileHash != nil { // 打log、上报告警等... fmt.Println("errFileHash: ", errFileHash) continue } // 文件没有变更,不用管 if fileHash == lastFileHash { continue } // 文件有变更 // 因为只有一个协裎在用这个变量,所以不存在竞争问题,直接赋值就好 lastFileHash = fileHash // 加载最新的配置 newConf, errNewConf := loadConfig(filePath) if errNewConf != nil { // 打log、上报告警等... fmt.Println("errNewConf: ", errNewConf) continue } // fmt.Println("newConf: ", newConf) // atomoc.Value Store configValue.Store(newConf) // 通知:配置已变更 cond.Broadcast() } } // 获取更新的配置,实际上可能有多个协裎在监听~ func loadConfigValue(configValue *atomic.Value, cond *sync.Cond) { for { cond.L.Lock() // 等待配置变更的信号 cond.Wait() // 读取新的配置信息,并且复制给全局变量 // s1 := configValue.Load() // fmt.Printf("s1: %T, %v \n", s1, s1) // Notice 注意这里要转化为 *Student StudentConfig = configValue.Load().(*Student) cond.L.Unlock() } } func main() { var configValue atomic.Value var cond = sync.NewCond(&sync.Mutex{}) // Notice 注意在协裎中要使用同一个 atomic.Value 变量,所以需要传指针! // check & store & broadcast go checkFileChanged(&configValue, cond, "./develop.json") // listening & load go loadConfigValue(&configValue, cond) // 在主协裎打印一下最新的配置看看 go func() { for { time.Sleep(time.Second * 2) fmt.Println("最新的配置:", StudentConfig) } }() select {} }main.go