Go语言之使用pprof工具查找goroutine(协程)泄漏
用pprof工具查找goroutine(协程)泄漏
goroutine泄漏指的是goroutine启动之后没有退出导致goroutine的数量持续上升,或者是在实际应用中goroutine占用了很长时间才退出导致在一段时间内goroutine的数量急剧上升。
通常可以采用Go自带的pprof工具来定位问题
如下面这个示例
这是一个简单的HTTP服务,当接收到请求时另起一个goroutine来输出日志,同时返回“Hello, World!\n”。
在记录日志之前可能要处理一个耗时很长的业务逻辑,如通过公网请求第三方的API接口,这里为了简化问题用time.Sleep来示意。
goroutineleak.go
package main import ( "io" "log" "net/http" _ "net/http/pprof" "time" ) func writeLog(msg string) { go func() { time.Sleep(1 * time.Second) log.Println(msg) }() } func handler(w http.ResponseWriter, r *http.Request) { writeLog(r.URL.RequestURI()) io.WriteString(w, "Hello, world!\n") } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
利用Grinder来压测,开8个进程,每个进程开3个线程,同时请求上述服务。
在请求过程中,通过浏览器查看http://pub.pengpengzhou.com:8080/debug/pprof/goroutine?debug=1, 域名是上述服务所在的地址。
从pprof的返回结果中,我们可以看到协程总数是5605,下面列出了5组产生协程的代码stack trace,按产生的协程数量倒排序。
可以看到第一组产生了5597个协程,产生位置在goroutineleak.go的13行“time.Sleep(1 * time.Second)”对应的函数是main.writeLog。
问题很快得到定位,把这一行注释掉,重新再压测,可以得到如下结果, 协程总数一下就降到了8,泄漏的问题得到了解决。
当然,在实际应用中,耗时是不可避免的,通常是用设置超时的办法来规避长时间等待。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
最新评论