记录 response.Body.Close() 引发的 goroutine 泄漏
某天下午三点,同事突然找我,因为新版本早上刚刚发布,线上所有机器 CPU 和 Memory 全部打满,ssh 连不上(因为打满的太快了,也没有 pprof 生成),紧急 rollback。rollback 之后,依然很快打满...
一边让 SRE 帮忙 terminal 机器,一边尝试生成 pprof,最终成功凭借 pprof 查出问题。
问题代码如下:
乍一看这里代码是没有问题的,然后搜了一下 error log,果然这里一直在报错(下游服务内存不足了还没来得及扩容,一直报错也没通知我们。不要好奇为什么不是 gRPC,因为个别服务是发的 http...)
所以猜测是 http.Do 里面开了某个 goroutine,等着执行 response.Body.Close()才会结束 goroutine。
我们先看看 close 方法做了什么(找了半天,真的难找,项目太大了,实现这个接口的真多)
通过上面 close 代码,看到只是在调用 bodyEOFSignal 对象里的一些方法,那么这些对象是怎么来的呢?显而易见是 http.Do(req)方法返回的 request body 的实现类(又要看最不想看的代码了),下面我们看看 http.Do 方法里面哪里出现了泄漏,我们直接给出代码
可以看到,http 会启动两个 goroutine 分别处理 readLoop 和 writeLoop。
下面我们看看 readLoop 做了哪些事
所以问题很清晰了,readLoop 和 writeLoop 两个 goroutine 在 写入请求并获取 response 返回后,并没有跳出 for 循环,而继续阻塞在下一次 for 循环的 select 语句里面,goroutine 一直无法被回收,cpu 和 memory 全部打满(整个 http 的代码太多了...有空慢慢分析吧)
版权声明: 本文为 InfoQ 作者【王博】的原创文章。
原文链接:【http://xie.infoq.cn/article/cb35190f32a644b99844cc26e】。文章转载请联系作者。
评论