“还是老样子,又是一个背景交代。
最近公司一直在搞降本增效,各种优化。期间发现一些服务的配置文件热加载经常更新失败,一番分析之后,发现是框架里使用了 viper 的文件监控和热加载的功能,在一些特殊的使用姿势的情况下,会引发更新bug。”

具体场景

关于热加载的使用方式,在脱敏之后的代码大概张下面这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package main

import (
"encoding/json"
"fmt"
"net/http"
"time"

"github.com/fsnotify/fsnotify"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"github.com/willas/overseer"
)

var (
FusingConfig cfg
)
type cfg struct {
Test item
}
type item struct {
List []int
}

func main() {
serverAddr := fmt.Sprintf(":8989")
overseer.Run(overseer.Config{
Program: prog,
Address: serverAddr,
Debug: true,
})
}

func prog(state overseer.State) {
confFile := "/data/demo/demo.toml"
fmt.Println("cfgFile: ", confFile)
LoadConfig(confFile)
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Op)
LoadConfig(confFile)
})
err := http.Serve(state.Listener, gin.New())
if err != nil {
fmt.Println("server start failed", err)
}
//等待以上结束
time.Sleep(time.Second)
}

func LoadConfig(cfgFile string) {
viper.SetConfigFile(cfgFile)
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
fmt.Println("viper read config error", err)
return
}
err = viper.Unmarshal(&FusingConfig)
if err != nil {
fmt.Println("viper unmarshal error", err)
return
}
fByte, _ := json.MarshalIndent(FusingConfig, "", " ")
fmt.Println("changed file content:\n", string(fByte))
}

配置文件

其中加载的配置文件内容如下:

1
2
[test]
list=[1,2,3]

bug的表现场景是:当对list数组元素进行删减时,无法正确的更新删减后的内容

截图说明

  • 二图胜千言:
    • 初次加载配置内容:
      图1

    • 可以看到热加载之后的配置文件内容没有符合预期。

      图2