前言
最近打算用三篇文章讲述一下Golang
利用性能剖析,本文是第一篇,先来介绍Go
语言自带的性能剖析库pprof
怎么应用,前面两篇会解说怎么用pprof
对Echo
或者Gin
框架开发的利用进行性能剖析以及如何应用pprof
对gRPC 服务进行性能剖析。
有趣味追更的同学欢送微信关注「网管叨bi叨」
Golang
是一个十分重视性能的语言,因而语言的内置库里就自带了性能剖析库pprof
。
性能剖析和采集在计算机性能调试畛域应用的术语是profile
,或者有时候也会应用profiling
代表性能剖析这个行为。所以pprof
名字里的prof
来源于对单词profile
的缩写,profile
这个单词的原意是画像,那么针对程序性能的画像就是利用应用 CPU 和内存等等这些资源的状况。都是哪些函数在应用这些资源?每个函数应用的比例是多少?
在Go
语言中,次要关注的程序运行状况包含以下几种:
- CPU profile:报告程序的 CPU 应用状况,依照肯定频率去采集应用程序在 CPU 和寄存器下面的数据
- Memory Profile(Heap Profile):报告程序的内存应用状况
- Block Profile:报告导致阻塞的同步原语的状况,能够用来剖析和查找锁的性能瓶颈
- Goroutine Profile:报告 goroutines 的应用状况,有哪些 goroutine,它们的调用关系是怎么的
工具型利用的性能剖析
如果你的利用是工具类利用,执行完工作就完结退出,能够应用 runtime/pprof
[1]库。
比方要想进行 CPU Profiling,能够调用 pprof.StartCPUProfile()
办法,它会对以后应用程序进行CPU应用状况剖析,并写入到提供的参数中(w io.Writer
),要进行调用 StopCPUProfile()
即可。
去除错误处理只须要三行内容,个别把局部内容写在 main.go
文件中,应用程序启动之后就开始执行:
1. f, err := os.Create(*cpuprofile) 2. ... 3. pprof.StartCPUProfile(f) 4. defer pprof.StopCPUProfile()
利用执行完结后,就会生成一个文件,保留了咱们利用的 CPU应用状况数据。
想要取得内存的数据,间接应用 WriteHeapProfile
就行,不必 start
和 stop
这两个步骤了:
1. f, err := os.Create(*memprofile) 2. pprof.WriteHeapProfile(f) 3. f.Close()
咱们的文章会把重点篇幅放在服务型利用的性能剖析,所以对于工具型利用的性能剖析就说这么多。
服务型利用性能剖析
如果你的利用是始终运行的,比方 web 利用或者gRPC
服务等,那么能够应用 net/http/pprof
库,它可能在利用提供 HTTP 服务时进行剖析。
如果应用了默认的 http.DefaultServeMux
(通常是代码间接应用 http.ListenAndServe("0.0.0.0:8000", nil)
),只须要在代码中增加一行,匿名援用net/http/pprof
:
import _ "net/http/pprof"
如果你应用自定义的 ServerMux
复用器,则须要手动注册一些路由规定:
1. r.HandleFunc("/debug/pprof/", pprof.Index) 2. r.HandleFunc("/debug/pprof/heap", pprof.Index) 3. r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) 4. r.HandleFunc("/debug/pprof/profile", pprof.Profile) 5. r.HandleFunc("/debug/pprof/symbol", pprof.Symbol) 6. r.HandleFunc("/debug/pprof/trace", pprof.Trace)
注册完后拜访http://localhost/debug/pprof
端点,它会失去相似上面的页面内容:
1. Types of profiles available: 2. Count Profile 3. // 上面是一些可拜访的/debug/pprof/目录下的路由 4. 2 allocs 5. 0 block 6. 0 cmdline 7. 36 goroutine 8. 2 heap 9. 0 mutex 10. 0 profile 11. 13 threadcreate 12. 0 trace 13. full goroutine stack dump 14. Profile Descriptions: 16. // 上面是对下面那些路由页面里展现的性能剖析数据的解释 17. allocs: A sampling of all past memory allocations 18. block: Stack traces that led to blocking on synchronization primitives 19. cmdline: The command line invocation of the current program 20. goroutine: Stack traces of all current goroutines 21. heap: A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample. 22. mutex: Stack traces of holders of contended mutexes 23. 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. 24. threadcreate: Stack traces that led to the creation of new OS threads 25. 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.
这个门路下几个须要重点关注的子页面有:
/debug/pprof/profile
:拜访这个链接会主动进行 CPU profiling,继续 30s,并生成一个文件供下载,能够通过带参数?=seconds=60
进行60秒的数据采集/debug/pprof/heap
:Memory Profiling 的门路,拜访这个链接会失去一个内存 Profiling 后果的文件/debug/pprof/block
:block Profiling 的门路/debug/pprof/goroutines
:运行的 goroutines 列表,以及调用关系
间接拜访这些页面产生的性能剖析数据咱们是剖析不过去什么的,Go在1.11
版本后在它自带的工具集go tool
里内置了pprof
工具来剖析由pprof
库生成的数据文件。
应用go tool pprof
通过下面的设置能够获取服务的性能数据后,接下来就能够应用go tool pprof
工具对这些数据进行剖析和保留了,个别都是应用pprof
通过HTTP
拜访下面列的那些路由端点间接获取到数据后再进行剖析,获取到数据后pprof
会主动让终端进入交互模式。在交互模式里pprof
为咱们提供了不少剖析各种指标的子命令,在交互模式下键入help
后就会列出所有子命令。
NOTE pprof子命令的应用办法能够参考 pprof --help
或者 pprof 文档[2]。
CPU性能剖析
进行CPU
性能剖析间接用go tool pprof
拜访下面说的/debug/pprof/profile
端点即可,等数据采集完会主动进入命令行交互模式。
1. ➜ go tool pprof http://localhost/debug/pprof/profile 2. Fetching profile over HTTP from http://localhost/debug/pprof/profile 3. Saved profile in /Users/Kev/pprof/pprof.samples.cpu.005.pb.gz 4. Type: cpu 5. Time: Nov 15, 2020 at 3:32pm (CST) 6. Duration: 30.01s, Total samples = 0 7. No samples were found with the default sample value type. 8. Try "sample_index" command to analyze different sample values. 9. Entering interactive mode (type "help" for commands, "o" for options) 10. (pprof)
默认采集时长是 30s,如果在 url 最初加上 ?seconds=60
参数能够调整采集数据的工夫为 60s。
采集实现咱们就进入了一个交互式命令行,能够对解析的后果进行查看和导出。能够通过 help
来查看反对的子命令有哪些。
NOTE: 如果pprof
用性能数据生成剖析图的话、包含前面的go-torch火焰图都依赖软件graphviz
Mac 用户间接
brew install graphviz
就能装置,其余零碎官网下载页面也有提供安装包,请拜访https://graphviz.org/download/
列出最耗时的中央
一个有用的命令是 topN
,它列出最耗时间的中央:
1. (pprof) top10 2. 130ms of 360ms total (36.11%) 3. Showing top 10 nodes out of 180 (cum >= 10ms) 4. flat flat% sum% cum cum% 5. 20ms 5.56% 5.56% 100ms 27.78% encoding/json.(*decodeState).object 6. 20ms 5.56% 11.11% 20ms 5.56% runtime.(*mspan).refillAllocCache 7. 20ms 5.56% 16.67% 20ms 5.56% runtime.futex 8. 10ms 2.78% 19.44% 10ms 2.78% encoding/json.(*decodeState).literalStore 9. 10ms 2.78% 22.22% 10ms 2.78% encoding/json.(*decodeState).scanWhile 10. 10ms 2.78% 25.00% 40ms 11.11% encoding/json.checkValid 11. 10ms 2.78% 27.78% 10ms 2.78% encoding/json.simpleLetterEqualFold 12. 10ms 2.78% 30.56% 10ms 2.78% encoding/json.stateBeginValue 13. 10ms 2.78% 33.33% 10ms 2.78% encoding/json.stateEndValue 14. 10ms 2.78% 36.11% 10ms 2.78% encoding/json.stateInString
每一行示意一个函数的信息。前两列示意函数在 CPU 上运行的工夫以及百分比;第三列是以后所有函数累加应用 CPU 的比例;第四列和第五列代表这个函数以及子函数运行所占用的工夫和比例(也被称为累加值 cumulative
),应该大于等于前两列的值;最初一列就是函数的名字。如果应用程序有性能问题,下面这些信息应该能通知咱们工夫都破费在哪些函数的执行上。
生成函数调用图
pprof
不仅能打印出最耗时的中央(top
),还能列出函数代码以及对应的取样数据(list
)、汇编代码以及对应的取样数据(disasm
),而且能以各种款式进行输入,比方 svg
、gif
、png
等等。
其中一个十分便当的是 web
命令,在交互模式下输出 web
,就能主动生成一个 svg
文件,并跳转到浏览器关上,生成了一个函数调用图(这个性能须要装置graphviz
后能力应用)。
图中每个方框对应利用程序运行的一个函数,方框越大代表函数执行的工夫越久(函数执行工夫会蕴含它调用的子函数的执行工夫,但并不是反比的关系);方框之间的箭头代表着调用关系,箭头上的数字代表被调用函数的执行工夫。
这里还要提两个比拟有用的办法,如果利用比较复杂,生成的调用图特地大,看起来很乱,有两个方法能够优化:
- 应用
web funcName
的形式,只打印和某个函数相干的内容 - 运行
go tool pprof
命令时加上--nodefration
参数,能够疏忽内存应用较少的函数,比方--nodefration=0.05
示意如果调用的子函数应用的 CPU、memory 不超过 5%,就疏忽它,不要显示在图片中。
剖析函数性能
想更粗疏剖析,就要准确到代码级别了,看看每行代码的耗时,间接定位到呈现性能问题的那行代码。pprof
也能做到,list
命令前面跟着一个正则表达式,就能查看匹配函数的代码以及每行代码的耗时:
1. (pprof) list podFitsOnNode 2. Total: 120ms 3. 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 4. 0 20ms (flat, cum) 16.67% of Total 5. . . 230: 6. . . 231:// Checks whether node with a given name and NodeInfo satisfies all predicateFuncs. 7. . . 232:func podFitsOnNode(pod *api.Pod, meta interface{}, info *schedulercache.NodeInfo, predicateFuncs map[string]algorithm.FitPredicate) (bool, []algorithm.PredicateFailureReason, error) { 8. . . 233: var failedPredicates []algorithm.PredicateFailureReason 9. . . 234: for _, predicate := range predicateFuncs { 10. . 20ms 235: fit, reasons, err := predicate(pod, meta, info) 11. . . 236: if err != nil { 12. . . 237: err := fmt.Errorf("SchedulerPredicates failed due to %v, which is unexpected.", err) 13. . . 238: return false, []algorithm.PredicateFailureReason{}, err 14. . . 239: } 15. . . 240: if !fit {
内存性能剖析
要想取得内存应用 Profiling 信息,只须要把数据源批改一下就行(对于 HTTP 形式来说就是批改 url 的地址,从 /debug/pprof/profile
改成 /debug/pprof/heap
):
1. ➜ go tool pprof http://localhost/debug/pprof/heap 2. Fetching profile from http://localhost/debug/pprof/heap 3. Saved profile in 4. ...... 5. (pprof)
和 CPU Profiling 应用一样,应用 top N
能够打印出应用内存最多的函数列表:
1. (pprof) top 2. 11712.11kB of 14785.10kB total (79.22%) 3. Dropped 580 nodes (cum <= 73.92kB) 4. Showing top 10 nodes out of 146 (cum >= 512.31kB) 5. flat flat% sum% cum cum% 6. 2072.09kB 14.01% 14.01% 2072.09kB 14.01% k8s.io/kubernetes/vendor/github.com/beorn7/perks/quantile.NewTargeted 7. 2049.25kB 13.86% 27.87% 2049.25kB 13.86% k8s.io/kubernetes/pkg/api/v1.(*ResourceRequirements).Unmarshal 8. 1572.28kB 10.63% 38.51% 1572.28kB 10.63% k8s.io/kubernetes/vendor/github.com/beorn7/perks/quantile.(*stream).merge 9. 1571.34kB 10.63% 49.14% 1571.34kB 10.63% regexp.(*bitState).reset 10. 1184.27kB 8.01% 57.15% 1184.27kB 8.01% bytes.makeSlice 11. 1024.16kB 6.93% 64.07% 1024.16kB 6.93% k8s.io/kubernetes/pkg/api/v1.(*ObjectMeta).Unmarshal 12. 613.99kB 4.15% 68.23% 2150.63kB 14.55% k8s.io/kubernetes/pkg/api/v1.(*PersistentVolumeClaimList).Unmarshal 13. 591.75kB 4.00% 72.23% 1103.79kB 7.47% reflect.Value.call 14. 520.67kB 3.52% 75.75% 520.67kB 3.52% k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.RegisterType 15. 512.31kB 3.47% 79.22% 512.31kB 3.47% k8s.io/kubernetes/pkg/api/v1.(*PersistentVolumeClaimStatus).Unmarshal
每一列的含意也是相似的,只不过从 CPU
应用时长变成了内存应用大小,就不多解释了。
相似的,web
命令也能生成 svg
图片在浏览器中关上,从中能够看到函数调用关系,以及每个函数的内存应用多少。
须要留神的是,默认状况下,统计的是内存应用大小,如果执行命令的时候加上 --inuse_objects
能够查看每个函数调配的对象数;--alloc-space
查看调配的内存空间大小。
go-torch 和火焰图
火焰图(Flame Graph)是 Bredan Gregg 创立的一种性能剖析图表,因为它的样子近似火焰而得名。下面的 profiling
后果也转换成火焰图,这里咱们要介绍一个工具:go-torch[3]。这是 uber 开源的一个工具,能够间接读取 pprof
的 profiling
数据,并生成一个火焰图的 svg 文件。
img
火焰图 svg 文件能够通过浏览器关上,它对于调用图的长处是:能够通过点击每个方块来剖析它下面的内容。
火焰图的调用程序从下到上,每个方块代表一个函数,它下面一层示意这个函数会调用哪些函数,方块的大小代表了占用 CPU 应用的长短。火焰图的配色并没有非凡的意义,默认的红、黄配色是为了更像火焰而已。
go-torch 工具的应用非常简单,没有任何参数的话,它会尝试从 http://localhost/debug/pprof/profile
获取 profiling 数据。它有三个罕用的参数能够调整:
-u --url
:要拜访的 URL,这里只是主机和端口局部-s --suffix
:pprof profile 的门路,默认为/debug/pprof/profile
--seconds
:要执行 profiling 的工夫长度,默认为 30s
要生成火焰图,须要当时装置 FlameGraph[4]工具,这个工具的装置很简略,只有把对应的可执行文件放到 $PATH
目录下就行。
总结
明天的文章把Go语言的性能剖析库pprof
的装置和应用办法大体流程走了一遍,重点解说了一下最罕用的几个性能剖析命令以及如何用pprof
采集的profile
数据找出程序里最消耗性能的局部。置信有了pprof
的帮忙在遇到须要优化程序性能的时候咱们能有更多的参照指标从而对症下药地对程序性能进行优化改良。
在应用pprof
采集数据的时候肯定要留神上面两点:
- 只有在有访问量的时候能力采集到这些性能指标数据。我是在公司的压测环境对接口压测时用
pprof
拿到的数据,如果你是在本地运行程序的话最好用Postman
或者Jmeter
这些工具做个简略的并发拜访。 - 除非有健全的安全策略,否则最好只在测试和压测环境加上
pprof
应用的那些路由,不要在生产环境上利用。
这篇文章就说这么多,前面的文章会介绍怎么在Echo
和Gin
框架下应用pprof
,以及如何用pprof
剖析gRPC
服务的性能。求关注、求点赞、求转发!我是网管,会在这里每周保持输入原创,咱们下期再见吧。
援用链接
[1]
runtime/pprof
: https://golang.org/pkg/runtim...
[2]
pprof 文档: https://github.com/google/ppr...
[3]
go-torch: https://github.com/uber/go-torch
[4]
FlameGraph: https://github.com/brendangre...
文章举荐
三种传递gRPC动静参数形式的应用体验
看Kubernetes源码,学习怎么用Go实现调度队列
- END -