今天简单说下 Response.Body.Close,当发起一个请求后,需要手动关闭此请求。
但,这个关闭的位置也有考究。
一开始,项目中的代码是如下顺序写的。
res, err := cli.Do(req)
defer res.Body.Close()
if err != nil {
fmt.Println(err.Error())
return
}
正常情况下,是不会遇到有问题的情况。在不定期检测线上的日志的时候,还是会发现,第二行那报空指针错误
invalid memory address or nil pointer dereference, goroutine 43 [running]:
终于在某次线上需求,需要临时调整个路由地址时,发现了原因。一开始不知道路由对应的域名对应的机房是做了环境隔离的,导致连不上,这时发现日志都是空指针错误。然后,在线下复现了下,也就是准备个不存在的地址,并且在第二行那打个断点,发起个请求,便看到
bad access: nil dereference
同时,也发现 err 的值为
context deadline exceeded (Client.Timeout exceeded while awaiting headers)
也就是说,此请求压根就没有发起或是连接失败,那么也就不用关闭了。
ok,接下来,就调整了顺序。
res, err := cli.Do(req)
if err != nil {
fmt.Println(err.Error())
return
}
defer res.Body.Close()
这时便不会报空指针错误,并且打印了错误原因。
到此为止了么,若是再思考下,为什么调整了顺序,就不报空指针错误呢,一开始,第二行用了 defer 了啊,应该是在程序的最后执行的啊,和调整后的有什么区别么?
这个得从 defer 执行机制说起,在程序执行到 return 后,defer 是逆序执行,典型的先进后出执行。
没调整顺序时,是先打印空指针错误,然后到 return,然后开始执行 defer 里的代码,由于请求没有发起成功,因此 res 为 nil,关闭时便会报空指针错误。
调整顺序后,是先打印空指针错误,然后到 return。注意,defer 是放到 return 之后,也就是此时是没有 defer 的执行语句的,也就不用执行了,也就不会报错了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)