背景
在某项目中,由于技术升级或者提升产品适用场景,需要对寻址参数进行修改,公司内寻址用的modid和cmdid两个整数统一使用字符串addr替代,便于兼容ipvs等。
为减少兼容代码,存储在zookeeper中的存量配置数据需要做结构转换,这活我干了。
故障代码
多么简单单纯的代码,每天都这样搬砖,职业生涯是道不了35岁的,也许30岁就可以退休了。
func (act *AccessCfgsTool) convert() { // 读出Task信息后,清理掉L5的两个整数后写回 var task mc_proto.Task for _, v := range act.taskIDs { //处理access_cfg配置 path := fmt.Sprintf("%s/%d", TASK_PATH, v) fmt.Printf("converting %s.\n", path) _, err := zkutil.ZkReadJson(act.conn.conn, path, &task) //做转换处理 XXXXXXXX err = zkutil.ZkUpdateJson(act.conn.conn, path, -1, &task) fmt.Printf("convert %s done.\n", path) } }
故障现象
在测试环境上线后,过了几天,一位大哥跟我说,数据怎么是乱的,为什么这个任务的配置上,会有其他任务的信息?
我蒙蔽了,但是无知让我无畏:怎么可能,一定是你们做了什么操作,搞乱了。
外强中干的我赶紧看看转换代码,发现了可能的原因,并简单的测试了一下。
func TestUnMarshal() { type T struct { A string B string } t := T{A: "hello,world,A"} str := "{\"B\":\"hello,world,B\"}" json.Unmarshal([]byte(str), &t) fmt.Printf("%v\n", t) } D:\xxx\src\paper>go run main.go {hello,world,A hello,world,B}
故障原因
到这里大概都知道故障的原因了,对转换代码做下修正。
func (act *AccessCfgsTool) convert() { // 读出Task信息后,清理掉L5的两个整数后写回 //移动到下面第二行 var task mc_proto.Task for _, v := range act.taskIDs { var task mc_proto.Task //处理access_cfg配置 path := fmt.Sprintf("%s/%d", TASK_PATH, v) fmt.Printf("converting %s.\n", path) _, err := zkutil.ZkReadJson(act.conn.conn, path, &task) //做转换处理 XXXXXXXX err = zkutil.ZkUpdateJson(act.conn.conn, path, -1, &task) fmt.Printf("convert %s done.\n", path) } }
问题解决了。
结论
golang json.Unmarshal()不会替我们清除已有变量的值,只会用解析出的值进行覆盖赋值。