上周因同事休假暂时帮忙维护一个服务,周五下午本以为要顺利度过本周的时候线上出了问题,在进行数据修复的时候发现之前一个工具代码的隐藏 bug,该 bug 会在进行数据修复时触发 panic。剥离具体业务逻辑之后代码逻辑如下:
1 | func main() { |
main 函数逻辑很简单:从数据库中查询某个数据,将该数据转换为map[string]interface{},再通过 JsonBuilder 根据JsonPath对数据进行修改,其中依赖的其他函数如下:
1 | func StructToMap(v interface{}) (map[string]interface{}, error) { |
在讨论为何会引发 panic 之前,有几个背景知识需要交代清楚:
json.Marshal(v any) ([]byte, error)函数如果入参为 nil 则返回的 bytes 内容为nulljson.Unmarshal(data []byte, v any)函数如果入参data为null则函数执行结束之后 V 的值为 nil- golang 的 interface 的内部实现两个字段:
type和data,只有两个字段都为 nil 时interface == nil才为true
这里引发 panic 的主要原因是StructToMap返回的 map 为 nil,在后续将其赋值给了builder.data, 而 data 为interface 类型,因此该interface的type为map[string]interface{},而该interface的data为 nil,最终导致if b.data == nil 的判断语句失效,进而没有进行数据初始化引发了 panic。
这里只需将if b.data == nil 修改为if b.data == nil || reflect.ValueOf(b.data).IsNil() 即可准确的判断出 interface是否为 nil,从而完成数据初始化避免 panic
golang 中的 inteface 是否为 nil 的判断已经是老生常谈的问题了,但在实际开发过程中还是经常被忽略掉,再搭配到 json 包一起使用,一不小心还是很容易踩坑。