go 配置文件热加载
最近在捣鼓golang,其中用线上数据全放在redis里,以前写死的配置有点不太适合了,热加载才是王道!
俗话说不会搜索的程序员不是一个好程序员(误
观摩了一下其他大佬的写法,大多都是采用定时器+goroutine
实现的。难度不大,开始码
配置文件结构体
假设一下我们的配置文件是json文件,那应该就是下面这样
// conf.json
{
"host": "127.0.0.1",
"port": 6379,
"passwd": "",
"db": 0
}
首先肯定是要写一个跟它一模一样的结构体出来
//json配置文件结构体
type Content struct {
Host string `json:"host"`
Port int `json:"port"`
Passwd string `json:"passwd"`
Db int `json:"db"`
}
如果继续在此结构体上编写操作函数不太稳妥,序列化json也是直接操作的指针。之前拆散它们的试错,最后还是让它们在一起了233
优化结构体
将文件名,同步锁,最后修改时间,配置文件结构体整合一起
//配置结构体
type Config struct {
Filename string
LastModifyTime int64
Lock *sync.RWMutex
Data interface{}
}
编写实例配置的工厂函数(这个有点约定俗成的规矩,具体出处你们自己去考证吧,好处挺多的)
func NewConfig(filename string, data interface{}) *Config {
conf := &Config{
Filename: filename,
Data: data,
Lock: &sync.RWMutex{},
}
conf.parse()
go conf.reload()
return conf
}
这里把配置结构体配置了默认参数,外部传入参数是配置文件的文件名和配置文件结构体,这里data的类型为接口类型,好处是Config
可以独立出来,代码多处复用。我们还使用了conf.parse()
方法,第一次解析文件,go conf.reload()
方法单独起一个goroutine跑(具体效果看后面),结果当然返回本体了
解析函数
暴力读取文件内容,放进json序列化。读文件出错没得商量退群吧你,解json出错返回失败,成功时锁住c.Data
写入
func (c *Config) parse() bool {
//记录最后修改时间
fileInfo, _ := os.Stat(c.Filename)
c.LastModifyTime = fileInfo.ModTime().Unix()
//读取文件内容
file, err := ioutil.ReadFile(c.Filename)
if err != nil {
log.Println("读取配置文件失败:", err)
//直接退出程序
os.Exit(1)
}
//解json
c.Lock.Lock()
if err = json.Unmarshal(file, &c.Data); err != nil {
log.Println("解析json出错:", err)
return false
}
log.Printf("文件内容 = %+v\n", c.Data)
return true
}
重载函数
这里新建了个5秒钟的定时器,隔5秒检查最后更改时间,与上次不同就再次解析,解析完成锁的释放
func (c *Config) reload() {
ticker := time.NewTicker(time.Second * 5)
for _ = range ticker.C {
func() {
fileInfo, _ := os.Stat(c.Filename)
currModifyTime := fileInfo.ModTime().Unix()
if currModifyTime > c.LastModifyTime {
if c.parse() {
log.Println("重新加载配置文件conf.json")
}
c.Lock.Unlock()
}
}()
}
}
运行
用着我们最开始使用的配置文件结构体
,NewConfig()
一下,这里要用select {}
阻塞一下程序,chan
同理,实时的更改conf.json
可以直接读到程序里更改了
func main() {
log.Println("开始运行...")
data := &Content{
Host: "127.0.0.1",
Port: 6379,
Passwd: "",
Db: 0,
}
_ = NewConfig("conf.json", data)
select {}
//end := make(chan bool, 1)
//<-end
}
代码
总结知识点
- io操作
- 同步
- 定时器
- 时间
- 通道
天呐,你咋还研究golang去了,我PY还没开始学呢
大佬可以py你吗( ꒪⌓꒪)
可啪,居然有大佬想py我