pprof是GoLang程序性能分析工具,prof是profile(画像)的缩写,用pprof我们可以分析下面9种数据
真正分析时常用4种
- CPU Profiling:CPU 分析,按照一定的频率采集所监听的应用程序 CPU(含寄存器)的使用情况,可确定应用程序在主动消耗 CPU 周期时花费时间的位置
- Memory Profiling:内存分析,在应用程序进行堆分配时记录堆栈跟踪,用于监视当前和历史内存使用情况,以及检查内存泄漏
- Block Profiling:阻塞分析,记录 goroutine 阻塞等待同步(包括定时器通道)的位置
- Mutex Profiling:互斥锁分析,报告互斥锁的竞争情况
做性能分析,第一步需要先获取数据,然后对数据进行分析。所以下面展示一下如何进行数据获取。
数据获取
数据获取的方法和应用的类型相关:
- 工具型应用:执行完任务就退出
- 服务型应用:一直运行,如web服务等
- 使用默认
ServerMux
- 自定义
ServerMux
- 开源web框架,如gin等
- grpc类服务
- 使用默认
工具形应用
工具形应用主要使用 runtime/pprof
库,将画像数据写入文件中。
1 | package main |
执行完后,会发现cpuprofile、memoryprofile文件,里面包含cpu、内存的画像。 runtime/pprof
直接支持这两种画像。
服务型应用
使用默认ServerMux
这种是指使用了默认的 http.DefaultServeMux
,通常是代码直接使用 http.ListenAndServe("0.0.0.0:8000", nil)
,第二个参数赋值为nil的情况。对于这种只需要在代码中添加一行,匿名引用**net/http/pprof
**。
1 | package main |
执行 http://localhost:9090/?url_long=111&url_long=222 可看到返回内容 “Hello 程序员麻辣烫!”。
执行 http://localhost:9090/debug/pprof/ 可看到画像信息,如图一所示。
使用自定义的ServerMux
这里解释一下ServerMux
,它是HTTP包中的一个结构体,里面存储了指定路径和该路径对应的处理函数
1 | type ServeMux struct { |
HTTP包中Server的hander默认就是DefaultServeMux
1 | type Server struct { |
http.ListenAndServe函数可以传递handler,如果handler不为nil,则说明研发自定义了 ServerMux
,否则用的是默认DefaultServeMux
1 | // ListenAndServe always returns a non-nil error. |
ListenAndServe最终会调用到ServeHTTP函数,根据路径找到对应的执行函数,整个流程结束
1 | func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { |
net/http/pprof
包中,有init函数
1 | func init() { |
所以如果使用默认ServerMux
,则不需要注册,但是如果使用自定义的ServerMux
,则需要增加注册后,才能获取到pprof。
1 | r.HandleFunc("/debug/pprof/", pprof.Index) |
这里就不编写代码了,因为自定义一个ServerMux
虽然难度不大,但是还是有些耗时的。大家知道对这种情况如何添加pprof即可。
操作完成后,执行 http://localhost:9090/debug/pprof/ 可看到画像信息,如图一所示。
开源web框架
开源web框架很多,因为常用gin,此处就讲述一下如何在gin框架中使用pprof。
在gin中使用pprof比较简单:
直接引入
Gin
项目组提供的gin-contrib/pprof
包调用pprof.Register(r)
1 | package main |
执行 http://localhost:8082/ping 可看到返回结果 “OK”
执行 http://localhost:8082/debug/pprof/ 可看到画像信息,如图一所示
之所以调用pprof.Register(r)后pprof相关路由可访问,是因为Register替我们做了路径注册的事情
1 | // Register the standard HandlerFuncs from the net/http/pprof package with |
该项目地址为:https://github.com/shidawuhen/asap
grpc类服务
上面的几种服务型应用都能通过链接直接访问,如果是grpc这种类型的服务,如何使用pprof呢?
pprof
做CPU分析原理是按照一定的频率采集程序CPU(包括寄存器)的使用情况,所以我们可以
- 在
gRPC
服务启动时,异步启动一个监听其他端口的HTTP
服务,通过这个HTTP
服务间接获取gRPC
服务的分析数据 - 因为gin使用默认的
ServerMux
(服务复用器),所以只要匿名导入net/http/pprof
包,这个HTTP
的复用器默认就会注册pprof
相关的路由
1 | package main |
github地址为:https://github.com/shidawuhen/grpcservice (以前学习时的demo竟然用到新文章上,开心)
执行 http://localhost:50052/debug/pprof/ 可看到画像信息,如图一所示
PS:通过分析上面几种类型,可以看出对于服务型应用,核心在于将pprof的路由注册到服务中,并能提供访问。
数据分析
虽然我们生成了数据,这些数据可以存储到文件里、也可以展示在浏览器中。
但是直接访问这些性能分析数据,我们是分析不过来什么的。Go在1.11
版本后在它自带的工具集go tool
里内置了pprof
工具来分析由pprof
库生成的数据文件。
使用go tool pprof
分析数据,主要有两种写法:
- 通过路径,如go tool pprof http://localhost:8082/debug/pprof/profile (进入命令行交互模式)
- 通过下载的文件,如go tool pprof cpuprofile (进入命令行交互模式)或者 go tool pprof -http=:9091 cpuprofile(进入web页面)
进入命令行交互模式后,可以使用help查看所有子命令,使用help <cmd|option>查看子命令使用方法。
1 | (pprof) help |
1 | (pprof) help top |
具体选择哪种根据自己需要使用,本文都是用通过路径的方案进行分析,使用https://github.com/shidawuhen/asap中的代码。
CPU Profiling
先来看profile,访问/debug/pprof/profile
这个链接会自动进行 CPU profiling,持续 30s,并生成一个文件供下载,可以通过带参数?=seconds=60
进行60秒的数据采集。
为了模拟请求,使用ab进行压测,ab -k -c 1 -t 180 -n 100000000 http://localhost:8082/limit/countreject
执行go tool pprof http://localhost:8082/debug/pprof/profile后,默认需要等30s才会显示交互
列出最耗时的地方
1 | (pprof) top 20 |
每一行表示一个函数的信息。
flat:函数在 CPU 上运行的时间
flat%:函数在CPU上运行时间的百分比
sum%:是从上到当前行所有函数累加使用 CPU 的比例,如第二行sum=48.52=28.79+19.73
cum:这个函数以及子函数运行所占用的时间,应该大于等于flat
cum%:这个函数以及子函数运行所占用的比例,应该大于等于flat%
最后一列:函数的名字
如果应用程序有性能问题,上面这些信息应该能告诉我们时间都花费在哪些函数的执行上。通过这些信息可以发现,redis操作所消耗的时间微乎其微。
生成函数调用图
在交互模式下输入 web
,就能自动生成一个 svg
文件,并跳转到浏览器打开,生成了一个函数调用图,不过需要安装graphviz
后才能使用,安装方法可参考 https://shidawuhen.github.io/2020/02/08/go-callvis/ 。
左上角方框内数据:表示显示的为cpu的画像。显示的节点在总共30.36s的抽样中,占30.06s,比例为99.01%。
图中每个方框对应应用程序运行的一个函数,方框越大代表函数执行的时间越久(函数执行时间会包含它调用的子函数的执行时间,但并不是正比的关系);方框之间的箭头代表着调用关系,箭头上的数字代表被调用函数的执行时间。具体细节可以参考:https://github.com/google/pprof/tree/master/doc#interpreting-the-callgraph
方框中显示的时间为总时间,findrunnable的总执行时间为11.24s,总时间占比为37.02%,只算函数自身执行时间为0.11s,总时间占比为0.36%=(0.11/11.24)*37.02%。调用函数5.49+4.52+0.23+0.82=11.06约等于11.24-0.11=11.13。
通过函数调用图,可以很直观的看出哪个函数耗时严重。
分析函数代码
当确定出哪个函数耗时之后,可以用pprof分析函数中的哪一行导致的耗时,使用子命令:list 函数名。
1 | (pprof) list CountReject |
可以看出,对于CountReject函数,耗时的位置主要在第22行,请求Redis的操作。
Memory Profiling
memory profiling主要查看程序当前活动对象内存分配。使用方法和CPU Profiling一样,
执行go tool pprof http://localhost:8082/debug/pprof/heap
列出最耗内存的地方
仍然使用top命令,可以看出最耗内存的位置是:golang.org/x/net/webdav.(*memFile).Write
1 | (pprof) top 20 |
每一列的含义和CPU Profiling中表达的一致,只不过这里显示的是内存。
生成函数调用图
使用web命名,能够生成函数调用图,只不过显示的内容为内存维度
在这个代码里,我使用了swagger,在所有环境里都是打开的,所以看一下这个消耗可以发现生产环境确实是没必要开,浪费空间。
分析函数代码
仍然使用list 函数名,如list runtime.malg
1 | (pprof) list runtime.malg |
可以看到内存主要消耗的位置。
数据分析总结
使用profile可以获取很多重要信息,cpu profiling、memory profiling使用也是最频繁的。分析的时候,需要先获取到数据,通过web发现耗时的函数,然后通过list找到具体位置。
其它的数据的分析和CPU、Memory基本一致。下面列一下所有的数据类型:
- http://localhost:8082/debug/pprof/ :获取概况信息,即图一的信息
- go tool pprof http://localhost:8082/debug/pprof/allocs : 分析内存分配
- go tool pprof http://localhost:8082/debug/pprof/block : 分析堆栈跟踪导致阻塞的同步原语
- go tool pprof http://localhost:8082/debug/pprof/cmdline : 分析命令行调用的程序,web下调用报错
- go tool pprof http://localhost:8082/debug/pprof/goroutine : 分析当前 goroutine 的堆栈信息
- go tool pprof http://localhost:8082/debug/pprof/heap : 分析当前活动对象内存分配
- go tool pprof http://localhost:8082/debug/pprof/mutex : 分析堆栈跟踪竞争状态互斥锁的持有者
- go tool pprof http://localhost:8082/debug/pprof/profile : 分析一定持续时间内CPU的使用情况
- go tool pprof http://localhost:8082/debug/pprof/threadcreate : 分析堆栈跟踪系统新线程的创建
- go tool pprof http://localhost:8082/debug/pprof/trace : 分析追踪当前程序的执行状况
总结
希望大家在生产环境中不会用到pprof,正常情况下,大部分问题都不需要用到pprof即可解决。使用pprof是有一定成本的,必须要想办法先能获取到数据。
如果真的遇到线上问题必须使用pprof,建议先想好要分析哪类数据。
pprof对程序的性能优化还是很有利的,获取数据后,可以快速定位到耗时较多的位置进行优化,而且也支持只打印和某个函数相关的命令,很人性化。
资料
- https: //studygolang.com/articles/26918#reply0
- Golang程序性能分析(一)pprof和go-torch
- Golang程序性能分析(二)在Echo和Gin框架中使用pprof
- Golang程序性能分析(三)用pprof分析gRPC服务的性能
- Golang 大杀器之性能剖析 PProf
- http://docscn.studygolang.com/pkg/runtime/pprof/
- https://github.com/google/pprof/blob/master/doc/README.md
- https://github.com/google/pprof/tree/master/doc
- pprof的使用
- 手把手教你使用pprof分析web项目(Golang)