Go - pprof性能分析 (手把手教学)
一、前言
应用程序在运行时,总是会出现一些你所意料不到的问题,像是跑着跑着突然报警,监控提示你进程 CPU 使用率过高、内存占用不断增大(疑似泄露)、临时内存大量申请后长时间内不滑,又或是 Goroutine 泄露、出现 Goroutine 数量暴涨,并且持续保持,甚至是莫名其妙在某次迭代发布后的数小时内出现了应用程序无法提供服务的问题...
- pprof 是用于可视化和分析性能分析数据的工具,pprof是性能调试工具,可以生成类似火焰图、堆栈图,内存分析图
- pprof 以 profile.proto 读取分析样本的集合,并生成报告以可视化并帮助分析数据(支持文本和图形报告)
通常线上服务的内存泄漏问题排查,大致步骤如下:
- 尝试复现问题
- 结合go pprof等工具分析内存使用情况,及对应的函数调用栈
- 发现问题所在
- 修复问题,验证问题是否解决
- 回归测试,打包上线
二、命令参数说明
go tool pprof
是对应的命令行指令。它的源数据既可以是一个http地址,也可以是已经获取到的profile文件。使用go tool pprof
命令时,既可以采用交互式终端,也可以采用web进行可视化分析。
- pprof可以分析以下9中数据:
Profile项 | 说明 | 详情 |
---|---|---|
allocs | 内存分配 | 从程序启动开始,分配的全部内存 |
block | 阻塞 | 导致同步原语阻塞的堆栈跟踪 |
cmdline | 命令行调用 | 当前程序的命令行调用 |
goroutine | gorouting | 所有当前 goroutine 的堆栈跟踪 |
heap | 堆 | 活动对象的内存分配抽样。您可以指定 gc 参数以在获取堆样本之前运行 GC |
mutex | 互斥锁 | 争用互斥锁持有者的堆栈跟踪 |
profile | CPU分析 | CPU 使用率分析。可以在url中,通过seconds指定持续时间(默认30s)。获取配置文件后,使用 go tool pprof 命令分析CPU使用情况 |
threadcreate | 线程创建 | 导致创建新操作系统线程的堆栈跟踪 |
trace | 追踪 | 当前程序的执行轨迹。可以在url中,通过seconds指定持续时间(默认30s)。获取跟踪文件后,使用 go tool trace 命令调查跟踪 |
三、数据采集
3.1 Web采集 (适用进程服务)
- 服务引入
使用net/http/pprof
进行采集,服务的启动代码中只需要引入 net/http/pprof
即可。
go
package main
import (
"fmt"
"net/http"
_ "net/http/pprof"
)
func HelloWorld(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello world")
}
func main() {
http.HandleFunc("/", HelloWorld)
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println(err)
}
}
注意:net/http/pprof
当引入该包后,会自动触发该包的init()函数,进行注册路由
go
func init() {
http.HandleFunc("/debug/pprof/", Index)
http.HandleFunc("/debug/pprof/cmdline", Cmdline)
http.HandleFunc("/debug/pprof/profile", Profile)
http.HandleFunc("/debug/pprof/symbol", Symbol)
http.HandleFunc("/debug/pprof/trace", Trace)
}
- web页面采集数据
启动运行代码自动注册路由,那么就可以通过浏览器访问:http://127.0.0.1:8080/debug/pprof/
go
allocs: 查看过去所有内存分配的样本,访问路径为 `$HOST/debug/pprof/allocs`。
block: 查看导致阻塞同步的堆栈跟踪,访问路径为 `$HOST/debug/pprof/block`。
cmdline: 当前程序的命令行的完整调用路径。
goroutine:查看当前所有运行的 goroutines 堆栈跟踪,访问路径为 `$HOST/debug/pprof/goroutine`。
heap: 查看活动对象的内存分配情况, 访问路径为 `$HOST/debug/pprof/heap`。
mutex:查看导致互斥锁的竞争持有者的堆栈跟踪,访问路径为 `$HOST/debug/pprof/mutex`。
profile: 默认进行 30s 的 CPU Profiling,得到一个分析用的 profile 文件,访问路径为 `$HOST/debug/pprof/profile`。
threadcreate: 查看创建新 OS 线程的堆栈跟踪,访问路径为 `$HOST/debug/pprof/threadcreate`。
当点击profile 或者 浏览器输入http://127.0.0.1:8080/debug/pprof/profile?debug=1 会自动进行采集数据,它会临时采集一个时间区间(比如30秒)内的对应性能数据。然后下载 profile
, 该文件没法直接打开查看,下面第4点数据分析会讲解如何分析
3.2 基准测试采集
在 使用 testing 库进行 benchmark 基准测试 这篇文章中,我们介绍了 benchmark 的使用方式。除了直接在命令行中查看测试的结果外,也可以生成 profile 文件
testing
支持生成 CPU、memory 和 block 的 profile 文件。
- -cpuprofile=$FILE
- -memprofile=$FILE
- -blockprofile=$FILE
go
// add.go
var datas []string
func add(str string) int {
data := []byte(str)
datas = append(datas, string(data))
return len(datas)
}
// add_test.go
import "testing"
func TestAdd(t *testing.T) {
_ = add("go pprof add text")
}
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
add("go pprof add text")
}
}
以下命令分别对应CPU分析和内存分析
go
go test -bench=. -cpuprofile=cpu.profile
go test -bench=. -memprofile=mem.profile
3.3 代码采集 (适用脚本工具)
一些命令行工具,执行完毕后直接退出的应用,这种不会常驻进程的,需要手动些代码进行采集。
使用runtime/pprof
包进行程序运行时分析
go
package main
import (
"fmt"
"os"
"runtime/pprof"
)
func main() {
cpuProfile, err := os.Create("./pprof/cpu_profile")
if err != nil {
fmt.Printf("创建文件失败:%s", err.Error())
return
}
defer cpuProfile.Close()
memProfile, err := os.Create("./pprof/mem_profile")
if err != nil {
fmt.Printf("创建文件失败:%s", err.Error())
return
}
defer memProfile.Close()
//采集CPU信息
pprof.StartCPUProfile(cpuProfile)
defer pprof.StopCPUProfile()
//采集内存信息
pprof.WriteHeapProfile(memProfile)
for i := 0; i < 100; i++ {
fmt.Println("pprof 工具型测试")
}
}
四、数据分析
4.1 web页面分析 (推荐)
再使用web页面进行分析采集的文件时,需要下载 graphviz
, 选择对应的系统版本进行下载,记得下载后讲安装的bin目录配置到环境变量中。安装这个主要是为了渲染性能文件
go
// 检测是否安装好了graphviz
dot --version
你可以采用该命令进行数据一下的命令进行数据分析:
go
// 直接分析profile文件 (-http是开启web端口浏览器访问)
go tool pprof -http=:8081 profile
// CPU耗时文件 (前提是使用的3.1的web采集的方式,这个方式会自动下载并分析文件,后面的这段url为当前服务的路由)
go tool pprof -http=:8081 http://127.0.0.1:8080/debug/pprof/profile
// 内存分配分析 (这个方式会自动下载并分析文件)
go tool pprof -http=:8081 http://127.0.0.1:8080/debug/pprof/allocs
访问访问web界面 :http://localhost:8081/ui/
4.2 终端分析
终端分析没有web界面直观,但是也可以通过终端交互进行分析
go
// 执行分析文件
go tool pprof cpu.pprof
go tool pprof http://127.0.0.1:8080/debug/pprof/profile
go tool pprof http://127.0.0.1:8080/debug/pprof/allocs