這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
設定檔熱更新是伺服器程式的一個準系統,通過熱更新可以不停機調整程式的配置,特別是在生產環境可以提供極大的便利,比如發現log打得太多了可以動態調高日誌等級,商務邏輯參數變化,甚至某個功能模組的開關等都可以動態調整。
每種語言都有自己的熱更新實現方式,在golang裡面我看到了有人採用了一種錯誤的實現方式,如下:
type Config struct { Test1 string `json:"Test1"` Test2 int `json:"Test2"`}var ( config *Config)func loadConfig() { f, err := ioutil.ReadFile("config.json") if err != nil { fmt.Println("load config error: ", err) } err = json.Unmarshal(f, &config) if err != nil { fmt.Println("Para config failed: ", err) }}func init() { loadConfig() fmt.Println("Load config: ", *config) s := make(chan os.Signal, 1) signal.Notify(s, syscall.SIGUSR2) go func() { for { <-s loadConfig() fmt.Println("ReLoad config: ", *config) } }()}
這種方式,的問題就在於config可能被多個routine同時訪問,那麼loadcofig的時候直接在config上做解析是有問題的。在上面例子中,還是用的最簡單的json配置,如果是其他更複雜的配置形式,如xml,需要自己寫解析函數(而不是一個json.Unmarshal)的時候更容易放大問題。
可以採用更新的時候對config加鎖來解決這個問題:
package mainimport ( "encoding/json" "fmt" "io/ioutil" "log" "os" "os/signal" "sync" "syscall")//用json配置測試type Config struct { Test1 string `json:"Test1:` Test2 int `json:"Test1:`}var ( config *Config configLock = new(sync.RWMutex))func loadConfig() bool { f, err := ioutil.ReadFile("config.json") if err != nil { fmt.Println("load config error: ", err) return false } //不同的配置規則,解析複雜度不同 temp := new(Config) err = json.Unmarshal(f, &config) if err != nil { fmt.Println("Para config failed: ", err) return false } configLock.Lock() config = temp configLock.Unlock() return true}func GetConfig() *Config { configLock.RLock() defer configLock.RUnlock() return config}func init() { if !loadConfig() { os.Exit(1) } //熱更新配置可能有多種觸發方式,這裡使用系統訊號量sigusr1實現 s := make(chan os.Signal, 1) signal.Notify(s, syscall.SIGUSR1) go func() { for { <-s log.Println("Reloaded config:", loadConfig()) } }()}func main() { select {}}
因為熱載入的時候,可能有很多未知的routine在使用config,通過切換配置的時候加鎖可以保證多個routine對config的正確操作,不過需要注意的是,擷取設定檔的時候也需要加鎖。
不知道在golang裡面,有沒有不加鎖的方案可以實現配置熱更新?