业务程序使用了 Golang 中的 viper 进行加载配置,
但在实际使用过程中因为读取了两次配置,同时反序列化了两次,
导致一些数组类型的字段出现了不符合预期的结果,如下测试程序所示。
经过排查才知道是因为 mapstructure decodeSlice
使用反射保留了第一次的数组大小,如果第一次的数组长度大于第二次的数组长度,多余的数组元素仍然会保留。
package main
import (
"bytes"
"fmt"
"github.com/spf13/viper"
)
var defaultConfigContent []byte = []byte(`
values:
- item1
- item2
- item3
- item4
server:
endpoint: http://example11.com
values:
- item1
- item2
- item3
- item4
`)
var overrideConfigContent []byte = []byte(`
values:
- item11
- item22
server:
endpoint: http://example22.com
values:
- item11
- item22
`)
type Config struct {
raw *viper.Viper
Values []string `mapstructure:"values"`
Server struct {
Endpoint string `mapstructure:"endpoint"`
Values []string `mapstructure:"values"`
} `mapstructure:"server"`
}
func main() {
raw := viper.NewWithOptions()
raw.SetConfigType("yaml")
config := &Config{
raw: raw,
}
if err := config.raw.MergeConfig(bytes.NewBuffer(defaultConfigContent)); err != nil {
panic(err)
}
// 两次序列化需要开启该注释代码
// if err := config.raw.Unmarshal(&config); err != nil {
// panic(err)
// }
if err := config.raw.MergeConfig(bytes.NewBuffer(overrideConfigContent)); err != nil {
panic(err)
}
if err := config.raw.Unmarshal(&config); err != nil {
panic(err)
}
fmt.Println("===> config ", config)
// 日志打印
// 序列化一次 (正确)
// ===> config &{0xc00014c380 [item11 item22] {http://example22.com [item11 item22]}}
// 序列化两次 (错误)
// ===> config &{0xc0001f2380 [item11 item22 item3 item4] {http://example22.com [item11 item22 item3 item4]}}
}