想要使程序在不重启的前提下读取更新的配置文件,探索了以下几种方式:
-
信号量触发更新
-
API手动触发更新
-
监听文件触发更新
-
使用第三方包
一、信号量触发更新
对系统进程调用监听,当接收到 syscal.SIGHUP 或者 syscal.SIGUSR1 信号时,调用reload() 函数对 config 进行重新读取赋值。
核心代码:
hup := make(chan os.Signal)
signal.Notify(hup, syscall.SIGHUP)
go func() {
for {
select {
case <-hup:
if err := reload(conf); err != nil {
log.Errorf("Error reloading config: %s", err)
}
}
}
}()
二、API手动触发更新
对系统预留RESTful API,使用curl -x POST 方式触发,reload()更新。
核心代码:
hup := make(chan bool)
go func() {
<- hup
for {
select {
case <-hup:
if err := reload(conf); err != nil {
log.Errorf("Error reloading config: %s", err)
}
}
}
}()
三、监听文件变化触发更新
利用ticker每隔一段时间检查config是否更新,如果更新则重新解析config。
此方式中有两种实现:
- lock
- atomic
1.lock 代码
func (c *Config) parse() bool {
fname, _ := os.Stat(c.Filename)
c.LastModifyTime = fname.ModTime().Unix()
f, err := ioutil.ReadFile(c.Filename)
if err != nil {
log.Println(err)
return false
}
data := new(MySQL)
err = json.Unmarshal(f, &data)
if err != nil {
log.Println(err)
return false
}
c.Mt.Lock()
c.MySQL = data
c.Mt.Unlock()
log.Printf("data: %+v\n", c.MySQL)
return true
}
func (c *Config) reload() {
ticker := time.NewTicker(time.Second * 3)
for {
select {
case <-ticker.C:
f, _ := os.Stat(c.Filename)
curModifyTime := f.ModTime().Unix()
if curModifyTime > c.LastModifyTime {
if c.parse() {
log.Println("loading...")
}
}
}
}
}
在线运行地址见文末
2.atomic 代码
var atoConfig atomic.Value
func (c *Config) reload() {
ticker := time.NewTicker(time.Second * 3)
for {
select {
case <-ticker.C:
f, _ := os.Stat(c.Filename)
curModifyTime := f.ModTime().Unix()
if curModifyTime > c.LastModifyTime {
mysql := read(c.Filename)
if mysql != nil {
atoConfig.Store(mysql)
chwr <- true
}
}
}
}
}
func (c *Config) write() {
data := atoConfig.Load().(*MySQL)
if data == nil {
return
}
c.MySQL = data
c.lastTime()
}
在线运行地址见文末
四、使用第三方包
有很多第三方包提供了比较完整的功能,比如go-micro/config、viper等。
viper的特性
-
设置默认值
-
可以读取如下格式的配置文件:JSON、TOML、YAML、HCL
-
监控配置文件改动,并热加载配置文件
-
从环境变量读取配置
-
从远程配置中心读取配置(etcd/consul),并监控变动
-
从命令行 flag 读取配置
-
从缓存中读取配置
-
支持直接设置配置项的值
从简单有效来讲,使用监听文件变化的atomic方式。代码更改比较少,也满足现有需求。
代码地址: https://play.golang.org/p/yUMsqZXlVrx https://play.golang.org/p/dxr6fT_mMR2