作為一個golang coder,使用golang編寫代碼是基本的要求。
能夠寫出代碼,並能夠熟悉程序執行過程中各方面的性能指標,則是更上一層樓。
如果在程序出現性能問題的時候,可以快速定位和解決問題,那么寫起代碼來,會更加自信。
本文介紹的pprof,是golang 自帶性能剖析工具,可以幫助定位程序中可能存在的問題。
1.profile文件的收集
pprof使用profile文件進行性能分析,profile文件是應用程序執行狀態的數據。
收集profile文件有兩種方式,對應go 語言提供的 runtime/pprof 和 net/http/pprof 兩個庫。
1.1 runtime/pprof
一般用於應用程序執行一段時間后,就會結束的情況。
例如,進行CPU profiling:
f, err := os.Create(*cpuprofile)
...
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
應用程序啟動時開始收集,應用程序結束后,就生成一個文件。
再例如,進行Mem profiling:
f, err := os.Create(*memprofile)
pprof.WriteHeapProfile(f)
f.Close()
生成一個mem profile文件。
1.2 net/http/pprof
這種方式主要用於程序一直在跑的場景。
如果使用了默認的 http.DefaultServeMux(通常是代碼直接使用 http.ListenAndServe("0.0.0.0:8000", nil)
),只需要添加一行:
import _ "net/http/pprof"
如果你使用自定義的 Mux,則需要手動注冊一些路由規則:
r.HandleFunc("/debug/pprof/", pprof.Index)
r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
r.HandleFunc("/debug/pprof/profile", pprof.Profile)
r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
r.HandleFunc("/debug/pprof/trace", pprof.Trace)
不管哪種方式,你的 HTTP 服務都會多出 /debug/pprof endpoint,訪問它會得到類似下面的內容:
/debug/pprof/
profiles:
0 block
62 goroutine
444 heap
30 threadcreate
full goroutine stack dump
下面的演示中,使用的例子代碼如下:
/*simple.go*/
package main
import (
"log"
_ "net/http/pprof"
"net/http"
"time"
)
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
go worker()
select{}
}
// simple worker
func worker(){
strSlice := []string{}
for {
str := "hello world "
strSlice = append(strSlice, str)
time.Sleep(time.Second)
}
}
代碼開始引入net/http/pprof
,在端口6060啟動http服務。
啟動應用程序
go build simple.go
./simpe
2.pprof分析profiling數據
pprof分析profiling數據主要有兩種方式:
- 交互式終端
- 頁面顯示
2.1 交互式終端
2.1.1 查看內存使用情況
使用heap profile查看內存使用情況。
go tool pprof http://localhost:6060/debug/pprof/heap
Fetching profile over HTTP from http://localhost:6060/debug/pprof/heap
Saved profile in /Users/lanyang/pprof/pprof.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz
Type: inuse_space
Time: Sep 21, 2019 at 1:56pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
(pprof) top10
Showing nodes accounting for 514kB, 100% of 514kB total
flat flat% sum% cum cum%
514kB 100% 100% 514kB 100% unicode.init
0 0% 100% 514kB 100% runtime.doInit
0 0% 100% 514kB 100% runtime.main
(pprof)
默認的Type是inuse_space
,即常駐內存.
與之對應的是alloc_objects
,表示臨時分配的內存。
列出top10的內存占用。
2.1.2 查看CPU使用情況
使用cpu profile查看cpu使用情況。
例如查看過去30s的cpu profile
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
Fetching profile over HTTP from http://localhost:6060/debug/pprof/profile?seconds=30
Saved profile in /Users/lanyang/pprof/pprof.samples.cpu.001.pb.gz
Type: cpu
Time: Sep 21, 2019 at 2:19pm (CST)
Duration: 30s, Total samples = 0
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
2.1.3 保存profile文件
從pprof使用上看,是首先保存profile文件,再進行分析的。
在一些場景,例如在線上環境,最好先保存profile,然后拿到線下做分析。
將profile文件保存下來:
wget http://localhost:6060/debug/pprof/heap
--2019-09-21 15:20:17-- http://localhost:6060/debug/pprof/heap
正在解析主機 localhost (localhost)... ::1, 127.0.0.1
正在連接 localhost (localhost)|::1|:6060... 失敗:Connection refused。
正在連接 localhost (localhost)|127.0.0.1|:6060... 已連接。
已發出 HTTP 請求,正在等待回應... 200 OK
長度:1162 (1.1K) [application/octet-stream]
正在保存至: “heap”
heap 100%[============================================================================================>] 1.13K --.-KB/s 用時 0s
2019-09-21 15:20:17 (111 MB/s) - 已保存 “heap” [1162/1162])
ll
-rw-r--r-- 1 lanyang staff 1.1K 9 21 15:20 heap
然后使用pprof進行分析,其中,./simple
是可執行文件,用於解析各種符號,./heap
是剛才獲取到的profile文件。
go tool pprof ./simple ./heap
Type: inuse_space
Time: Sep 21, 2019 at 3:20pm (CST)
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
2.2 頁面顯示
2.2.1 瀏覽器打開pprof頁面
瀏覽器中打開http://localhost:6060/debug/pprof/
如圖所示,列出了很多監控項,
Profile Descriptions:
allocs:
A sampling of all past memory allocations
block:
Stack traces that led to blocking on synchronization primitives
cmdline:
The command line invocation of the current program
goroutine:
Stack traces of all current goroutines
heap:
A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.
mutex:
Stack traces of holders of contended mutexes
profile:
CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.
threadcreate:
Stack traces that led to the creation of new OS threads
trace:
A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.
可以通過鏈接跳轉到對應界面。
2.2.2. 更詳細的頁面顯示
go 從1.11開始的pprof提供了更豐富性能數據展示方式,包括火焰圖,直接使用如下命令:
$ go tool pprof -http=":8081" [binary] [profile]
會打開瀏覽器頁面。
端口可以自己選擇,這里使用了8081。
binary
是應用程序的可執行文件,讀取符號信息。
profile
是profile文件,可以是本地文件,或者http地址。
例如,使用本地保存的profile文件:
$ go tool pprof -http=":8081" ./simple ./heap
或者,通過http 的profile:
go tool pprof -http=":8081" ./simple http://localhost:6060/debug/pprof/heap
Fetching profile over HTTP from http://localhost:6060/debug/pprof/heap
Saved profile in /Users/zhangyunyang/pprof/pprof.simple.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz
Serving web UI on http://localhost:8081
火焰圖如圖所示,示例代碼比較簡單,所以火焰圖不是很明顯。
如果不能使用go1.11,則可以使用最新的pprof工具:
# Get the pprof tool directly
$ go get -u github.com/google/pprof
$ pprof -http=":8081" [binary] [profile]