0%

如何使用 pprof 调试 golang 应用

Golang 是一个对性能要求很高的语言,
因此语言中自带的 pprof 工具成为我们检测 Golang 开发应用性能的利器。

Google 开发的 Golang 自 2009 年推出,已经日趋成为各大公司开发后端服务使用的语言,有名的基于 Golang 的开源项目有 Docker、Kubernetes 等。当使用 Golang 开发服务后端服务时,难免产生性能问题,如内存泄漏、Goroutine 卡死等,Golang 是一个对性能要求很高的语言,因此语言中自带的 pprof 工具成为我们检测 Golang 开发应用性能的利器。

Profiling 一般翻译为 画像,在计算机领域,我们可以将其理解为当前应用状态的画像。当程序性能不佳时,我们希望知道应用在 什么地方 耗费了 多少 CPU、memory。下面将介绍如何使用这一工具。

Golang 服务主要关注的应用运行情况包含以下几种

  • CPU profile:报告程序的 CPU 使用情况,按照一定频率去采集应用程序在 CPU 和寄存器上面的数据
  • Memory Profile(Heap Profile):报告程序的内存使用情况
  • Block Profiling:报告 goroutines 不在运行状态的情况,可以用来分析和查找死锁等性能瓶颈
  • Goroutine Profiling:报告 goroutines 的使用情况,有哪些 goroutine,它们的调用关系是怎样的

两种使用方式

go 语言提供了 runtime/pprofnet/http/pprof 两个库,这部分我们讲讲它们的用法以及使用场景。

工具型应用

工具型应用: 如果应用是命令行应用,程序启动后,在一段时间(短期运行)内会随着处理完成而关闭。

工具型应用使用使用 runtime/pprof 库, 可以产生 dump 文件,应用退出的时候把 profiling 的报告保存到文件中,进行分析。

runtime/pprof 导入 main.go

1
import "runtime/pprof"

其中 pprof 封装了很好的接口供我们使用,比如要想进行 CPU Profiling,可以调用 pprof.StartCPUProfile() 方法,它会对当前应用程序进行 CPU profiling,并写入到提供的参数中(w io.Writer),要停止调用 StopCPUProfile() 即可。

去除错误处理只需要以下内容,一般把部分内容写在 main.go 文件中,应用程序启动之后就开始执行:

1
2
3
4
f, err := os.Create(*cpuprofile)
...
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()

应用执行结束后,就会生成一个文件,保存了我们的 CPU profiling 数据。

想要获得内存的数据,直接使用 WriteHeapProfile 就行,不用 start 和 stop 这两个步骤了:

1
2
3
f, err := os.Create(*memprofile)
pprof.WriteHeapProfile(f)
f.Close()

服务型应用

服务型应用: 如果应用是一直运行(长期运行)的,比如 web 应用,那么可以使用 net/http/pprof 库,它能够在提供 HTTP 服务进行分析。

服务型应用使用 net/http/pprof 库, 其中 net/http/pprof 使用 runtime/pprof 包来进行封装,并在 http 端口上暴露出来。

如果使用了默认的 http.DefaultServeMux(通常是代码直接使用 http.ListenAndServe("0.0.0.0:8000", nil)),只需要添加一行:

1
import _ "net/http/pprof"

如果应用使用了自定义的 Mux,则需要手动注册一些路由规则:

1
2
3
4
5
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://127.0.0.1:8000/debug/pprof ,有以下输出

1
2
3
4
5
6
7
8
9
/debug/pprof/

profiles:
0 block
62 goroutine
444 heap
30 threadcreate

full goroutine stack dump

这个路径下还有几个子页面:

/debug/pprof/profile:访问这个链接会自动进行 CPU profiling,持续 30s,并生成一个文件供下载
/debug/pprof/heap: Memory Profiling 的路径,访问这个链接会得到一个内存 Profiling 结果的文件
/debug/pprof/block:block Profiling 的路径

获取和分析 Profiling 数据

获取的 Profiling 数据是动态的,要想获得有效的数据,请保证应用处于较大的负载(比如正在生成中运行的服务,或者通过其他工具模拟访问压力)。否则如果应用处于空闲状态,得到的结果可能没有任何意义。

能通过对应的库获取想要的 Profiling 数据之后(不管是文件还是 http),下一步就是要对这些数据进行保存和分析,我们可以使用 go tool pprof 命令行工具。

  • /debug/pprof/profile
  • /debug/pprof/heap
  • /debug/pprof/block
  • /debug/pprof/goroutines

go tool pprof 工具可以对以上各个路径 prof 文件进行更详细的分析,可以生成调用关系图和火焰图。

生成调用关系图

  • 安装 graphviz 工具
  • 分析工具使用命令 go tool pprof [binary][source]
  • 生成关系调用图
  1. 安装 graphviz 工具

    1
    2
    3
    4
    5
    # Centos 6/7
    # yum install -y graphviz

    # Centos 8
    # dnf install -y graphviz
  2. 分析工具使用命令 go tool pprof

    go tool pprof demo demo.prof
    (pprof) web  #生成调用关系图,demo.svg文件