背景
周所周知龙誊,內(nèi)存泄漏是一個(gè)很嚴(yán)重的問題,會(huì)導(dǎo)致系統(tǒng)運(yùn)行占用資源越來越多喷楣,無法釋放趟大,直至崩潰。所幸 Go 語言是一門 具有垃圾回收的語言铣焊,能大大降低遇到內(nèi)存泄漏的概率逊朽,而我們今天要說的是另外一個(gè)棘手的問題:協(xié)程泄漏。
協(xié)程泄漏:顧名思義就是出現(xiàn)了應(yīng)該釋放而沒有被釋放的協(xié)程曲伊,導(dǎo)致系統(tǒng)協(xié)程數(shù)量一直上升叽讳。不像對(duì)象回收需要引用計(jì)數(shù)、三色標(biāo)記等手段坟募,協(xié)程的回收是相當(dāng)簡(jiǎn)單的岛蚤,等待協(xié)程將代碼從頭到尾執(zhí)行完畢之后這一塊兒空間就會(huì)自動(dòng)回收,通常協(xié)程泄漏問題都是因?yàn)槟扯未a卡住了懈糯,陷入了死循環(huán)或者再等待一個(gè)不可能的條件等原因涤妒。
那么具體如何定位到到底是哪里出了問題呢,具體就要用到 Go 官方提供的性能分析工具了:pprof赚哗。
pprof
pprof 具體在 Go 語言中的實(shí)現(xiàn)是在包:runtime/pprof 中她紫,提供了諸如內(nèi)存分析硅堆、CPU分析、鎖分析等函數(shù)供調(diào)用贿讹,調(diào)用這個(gè)庫之后會(huì)將性能數(shù)據(jù)以 protobuffer 這種二進(jìn)制序列化格式導(dǎo)出渐逃。同時(shí)考慮到這一個(gè)庫較為底層,Go 官方在 runtime/pprof 上包裝提供了一個(gè)更加易用的庫:net/http/pprof民褂,提供了一種通過 HTTP 協(xié)議和性能數(shù)據(jù)交互的能力茄菊。除了性能數(shù)據(jù)的抓取工具,Golang 也提供了官方的性能數(shù)據(jù)分析工具:go tool pprof赊堪,下面我們將基于這些工具進(jìn)行一次協(xié)程泄漏問題的排查买羞。
制造協(xié)程泄漏
http.pprof 會(huì)在 init 的時(shí)候注冊(cè)一些路由到 http.DefaultServeMux 上,所以要使用這個(gè)庫雹食,我們要做的只需要引入這個(gè)包,并且使用 DefaultServeMux 監(jiān)控一個(gè)端口即可期丰,具體的話調(diào)用:http.ListenAndServe 時(shí) handler 傳空即可群叶,傳空的時(shí)候會(huì)默認(rèn)使用 DefaultServeMux 來處理。
一下是一個(gè)協(xié)程泄漏的例子钝荡,分別在第 11 行和 14 行泄漏了 1000 個(gè)協(xié)程街立。
package main
import (
"net/http"
_ "net/http/pprof"
)
func main() {
for i := 0; i < 1000; i++ {
go func() {
select {} // 泄漏了 1000 個(gè)協(xié)程
}()
go func() {
select {} // 泄漏了 1000 個(gè)協(xié)程
}()
}
// 啟動(dòng)一個(gè) pprof http server
if err := http.ListenAndServe(":7899", nil); err != nil {
panic(err.Error())
}
}
下面我們把這個(gè)服務(wù)跑起來:go run pprof.go
分析泄漏問題
我們先打開協(xié)程 Debug 的地址,我的服務(wù)監(jiān)聽在本地 7899 端口埠通,所以地址是:http://127.0.0.1:7899/debug/pprof/goroutine?debug=1赎离,通過這個(gè)我們已經(jīng)可以很明顯看到,我們的問題就發(fā)生在 pprof.go 11 行和 14 行這里端辱,泄漏了 2000 個(gè)協(xié)程梁剔,那么我們就重點(diǎn)去排查這一段代碼即可。
這個(gè)頁面很簡(jiǎn)單舞蔽,可讀性不高荣病,不過當(dāng)程序內(nèi)存狀態(tài)很復(fù)雜的時(shí)候,可以考慮配合可視化工具使用渗柿。
輸入命令:go tool pprof -http=:8001 http://127.0.0.1:7899/debug/pprof/goroutine\?debug\=1
在本地 8001 端口啟動(dòng)一個(gè) HTTP 可視化分析工具个盆,打開 http://127.0.0.1:8001/ui/ 地址,我們就能更直觀的看到所有的協(xié)程啟動(dòng)的情況:
同時(shí)這個(gè)可視化分析工具還支持火焰圖朵栖、類 TOP 圖表等格式進(jìn)行分析颊亮,點(diǎn)擊左上角 VIEW 切換即可:
總結(jié)
pprof 可以很方便幫助我們排查協(xié)程泄漏問題,同時(shí)這套工具能干的不止如此陨溅,幾乎所有在程序性能上遇到的問題都可以使用其進(jìn)行分析终惑,想了解其他功能的話可以參考以下官方文章進(jìn)行進(jìn)一步的學(xué)習(xí):
https://blog.golang.org/pprof
https://github.com/google/pprof/blob/master/doc/README.md
原文發(fā)表于:https://huweicai.com/troubleshotting-goroutine-leeking/