目录
什么是 Profile?
在计算机性能调试领域里,profile 就是对应用的画像,这里画像就是应用使用 CPU 和内存等情况,也就是说应用使用了多少 CPU 资源、都是哪些部分在使用、每个函数使用的比例是多少、有哪些函数在等待 CPU 资源等等。知道了这些,我们就能对应用进行规划,也能快速定位性能瓶颈。
Golang 是一个对性能特别看重的语言,因此语言中自带了 profile 的库,这篇文章就要讲解怎么在 golang 中做 profile。
在 Golang 中,主要关注的应用运行情况主要包括以下几种:
- CPU profile:报告程序的 CPU 使用情况,按照一定频率去采集应用程序在 CPU 和寄存器上面的数据
- Memory profile(Heap profile):报告程序的内存使用情况
- Block profile:报告 goroutines 不在运行状态的情况,可以用来分析和查找死锁等性能瓶颈
- Goroutine profile:报告 goroutines 的使用情况,有哪些 goroutine,它们的调用关系是怎样的
runtime/pprofnet/http/pprof
工具型应用
如果你的应用是一次性的,运行一段时间就结束,那么最好的办法就是在应用退出时把 profile 的报告保存到文件中,进行分析。对于这种情况,可以使用 runtime/pprof 库。
pprofpprof.StartCPUProfile()w io.WriterStopCPUProfile()
main.go
f, err := os.Create(*cpuprofile)
...
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
应用执行结束后,就会生成一个文件,保存了我们的 CPU profile 数据。
WriteHeapProfilestartstop
f, err := os.Create(*memprofile)
pprof.WriteHeapProfile(f)
f.Close()
服务型应用
net/http/pprof
在 import 里添加一行:
import _ "net/http/pprof"
在主函数中启动服务监听端口:
go func() {
http.ListenAndServe(":6060", nil)
}()
/debug/pprof
/debug/pprof/
profiles:
0 block
756 goroutine
16100 heap
0 mutex
94 threadcreate
full goroutine stack dump
go tool ppof 获取和分析 profile 数据
go tool pprof
graphviz
$ sudo apt-get install -y graphviz
注意获取的 profile 数据是动态的,要想获得有效的数据,请保证应用处于较大的负载(比如正在运行的服务,或者通过其他工具模拟访问压力)。否则如果应用处于空闲状态,得到的结果可能没有任何意义。
我们以 CPU profile 分析为例介绍两种分析方法。
终端
go tool pprofgo tool pprof [binary] [source]binarysource
➜ go tool pprof ./hyperkube http://172.16.3.232:10251/debug/pprof/profile
Fetching profile from http://172.16.3.232:10251/debug/pprof/profile
Please wait... (30s)
Saved profile in /home/cizixs/pprof/pprof.hyperkube.172.16.3.232:10251.samples.cpu.002.pb.gz
Entering interactive mode (type "help" for commands)
(pprof)
?seconds=60help
topN
(pprof) top10
130ms of 360ms total (36.11%)
Showing top 10 nodes out of 180 (cum >= 10ms)
flat flat% sum% cum cum%
20ms 5.56% 5.56% 100ms 27.78% encoding/json.(*decodeState).object
20ms 5.56% 11.11% 20ms 5.56% runtime.(*mspan).refillAllocCache
20ms 5.56% 16.67% 20ms 5.56% runtime.futex
10ms 2.78% 19.44% 10ms 2.78% encoding/json.(*decodeState).literalStore
10ms 2.78% 22.22% 10ms 2.78% encoding/json.(*decodeState).scanWhile
10ms 2.78% 25.00% 40ms 11.11% encoding/json.checkValid
10ms 2.78% 27.78% 10ms 2.78% encoding/json.simpleLetterEqualFold
10ms 2.78% 30.56% 10ms 2.78% encoding/json.stateBeginValue
10ms 2.78% 33.33% 10ms 2.78% encoding/json.stateEndValue
10ms 2.78% 36.11% 10ms 2.78% encoding/json.stateInString
累加值 cumulative
toplist
(pprof) list podFitsOnNode
Total: 120ms
ROUTINE ======================== k8s.io/kubernetes/plugin/pkg/scheduler.podFitsOnNode in /home/cizixs/go/src/k8s.io/kubernetes/_output/local/go/src/k8s.io/kubernetes/plugin/pkg/scheduler/generic_scheduler.go
0 20ms (flat, cum) 16.67% of Total
. . 230:
. . 231:// Checks whether node with a given name and NodeInfo satisfies all predicateFuncs.
. . 232:func podFitsOnNode(pod *api.Pod, meta interface{}, info *schedulercache.NodeInfo, predicateFuncs map[string]algorithm.FitPredicate) (bool, []algorithm.PredicateFailureReason, error) {
. . 233: var failedPredicates []algorithm.PredicateFailureReason
. . 234: for _, predicate := range predicateFuncs {
. 20ms 235: fit, reasons, err := predicate(pod, meta, info)
. . 236: if err != nil {
. . 237: err := fmt.Errorf("SchedulerPredicates failed due to %v, which is unexpected.", err)
. . 238: return false, []algorithm.PredicateFailureReason{}, err
. . 239: }
. . 240: if !fit {
disasm
可视化
web 命令svg
这个调用图包含了更多的信息,而且可视化的图像能让我们更清楚地理解整个应用程序的全貌。图中每个方框对应一个函数,方框越大代表执行的时间越久(包括它调用的子函数执行时间,但并不是正比的关系);方框之间的箭头代表着调用关系,箭头上的数字代表被调用函数的执行时间。
encoding/json.(*decodeState).objectdisadm 的用法相同,它。
pdf 命令pdfpprof --help
另一个可视化的方法是直接启动一个 http 服务:
go tool pprof -http="10.224.27.152:8081" ./hyperkube http://172.16.3.232:10251/debug/pprof/profile
在浏览器上访问 10.224.27.152:8081 即可看到各种界面。
注:本文大量使用了 使用 pprof 和火焰图调试 golang 应用 的内容,并结合了笔者平时的实践。