通常我们更新应用程序的配置文件,都需要手动重启程序或手动重新加载配置。重启服务会造成服务的短暂不可以用。所以我们要实现配置文件的热加载。实现这个的主要思路就是监听这个配置文件是否有改动
主要实现代码如下
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"sync"
"time"
)
// Config 用json配置测试
type Config struct {
filename string
lastModifyTime int64
Key string `json:"key"`
}
var (
config *Config
configLock = new(sync.RWMutex)
)
func (config *Config) reload() {
// 定时器
ticker := time.NewTicker(time.Second * 5)
for range ticker.C {
// 打开文件
func() {
f, err := os.Open(GetConfig().filename)
if err != nil {
fmt.Printf("open file error:%s\n", err)
return
}
defer f.Close()
fileInfo, err := f.Stat()
if err != nil {
fmt.Printf("stat file error:%s\n", err)
return
}
// 或取当前文件修改时间
curModifyTime := fileInfo.ModTime().Unix()
if curModifyTime > GetConfig().lastModifyTime {
// 重新解析时,要考虑应用程序正在读取这个配置因此应该加锁
// 使用了 configLock 全局锁
fmt.Print("cfg change, load new cfg\n")
loadConfig()
GetConfig().lastModifyTime = curModifyTime
}
}()
}
}
func loadConfig() bool {
fmt.Print("load cfg\n")
f, err := ioutil.ReadFile(config.filename)
if err != nil {
fmt.Println("load config error: ", err)
return false
}
//不同的配置规则,解析复杂度不同
temp := new(Config)
err = json.Unmarshal(f, &temp)
if err != nil {
fmt.Println("Para config failed: ", err)
return false
}
temp.filename = GetConfig().filename
temp.lastModifyTime = GetConfig().lastModifyTime
fmt.Printf("now cfg:%#v\n", temp)
configLock.Lock()
config = temp
configLock.Unlock()
return true
}
// GetConfig ...
func GetConfig() *Config {
configLock.RLock()
defer configLock.RUnlock()
return config
}
func init() {
config = new(Config)
config.filename = "config.json"
if !loadConfig() {
os.Exit(1)
}
//热更新配置可能有多种触发方式,这里使用定时器实现
go config.reload()
}
func main() {
ticker := time.NewTicker(time.Second * 1)
for range ticker.C {
fmt.Printf("key:%s\n", GetConfig().Key)
}
}