golang http 包 Response.Body.Close

今天简单说下 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 的执行语句的,也就不用执行了,也就不会报错了。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>