作者: yanhengwang,腾讯 PCG 开发工程师
在 golang 中创建 goroutine 是一件很容易的事情,但是不合理的使用可能会导致大量 goroutine 无法结束,资源也无法被释放,随着时间推移造成了内存的泄漏。避免 goroutine 泄漏的关键是要合理管理 goroutine 的生命周期,通过导出 runtime 指标和利用 pprof 可以发现和解决 goroutine 泄漏问题。
笔者维护了一个通过 SSH 连接到目标机器并执行命令的服务,这是一个内部小服务,平时没有问题的时候一般也不会关注。大约 4 个月前,最后一次更新的时候,增加了一个任务计数器并且导出到 prometheus 中监控起来。近期发现这个计数器在稳步增加。
![](https://i-blog.csdnimg.cn/blog_migrate/3c62c10e0c833b99409fe5375eac60bf.png)
第一反应是,好事!调用量稳步增长了!!但是一想不对啊,这内部小服务哪儿来这么多调用量。于是再看看 goroutine 的监控情况(这个数据从 runtime.NumGoroutine()获取的)
![](https://i-blog.csdnimg.cn/blog_migrate/6eae971e42759f4996127adc1f7ae5c5.png)
goroutine 的数量也是稳步增加的,单位时间请求量增加,goroutine 数量也增进,没毛病。但是又再转念一想,内部小服务,不可能不可能。
于是再看一下所有请求在 mm 系统的视图:
![](https://i-blog.csdnimg.cn/blog_migrate/9f2d07ca598cd736e9ef56cdacde5bbe.png)
可以看出,每 5 分钟请求量在 2000 左右,平均下来每分钟 400 的请求量,上面 prometheus 监控图中,每个曲线是一个实例,实际上部署了 4 个实例,因此 400 还要除以 4 得到单个实例(曲线)的请求量应该在 100/min 左右,在服务刚启动的时候该计数器也确实在 100/min 左右,随着时间推移慢慢泄漏了。
Goroutine 泄漏 (Goroutine leak)
虽然心里想着 99%是泄漏了,但是也要看点详细的信息。之前在服务里已经启用了 net/http/pprof,因此直接请求 pprof 暴露出来的 HTTP 接口。
# goroutines摘要
curl http://service:port/debug/pprof/goroutine?debug=1
# goroutines详情
curl http://service:port/debug/pprof/goroutine?debug=2
先看一下导出的 goroutine 摘要:
![](https://i-blog.csdnimg.cn/blog_migrate/7b67677367ab43b0d2fdb9379bcb9a4b.png)
有 1000+个 goroutine 处于同一个状态,简单看是等待读数据,再看下导出的 goroutine 详情: