Golang 配置文件热加载

通常我们更新应用程序的配置文件,都需要手动重启程序或手动重新加载配置。重启服务会造成服务的短暂不可以用。所以我们要实现配置文件的热加载。实现这个的主要思路就是监听这个配置文件是否有改动

主要实现代码如下

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)
	}
}