Golang内置了一些性能分析工具,可以将性能分析的结果文件输出,我们可以使用图形化的工具查看分析结果,在使用这些工具之前,我们需要安装一些工具,以便于查看分析文件。
为了支持查看图形化分析结果,首先需要安装graphviz工具:
代码语言:javascript复制brew install graphviz
为了方便调用Golang的分析工具,我们需要将$GOPATH/bin加入到$PATH中去:
- 在Windows中通过环境变量设置
- 在Mac下,通过vim ~/.bash_profile打开设置文件,添加配置如下:
uber提供了go-torch工具可以将分析结果文件以火焰图的方式进行展示,在go1.11之后的版本中go-torch被内置到了golang中,当然要是本地没有go-torch工具,可以通过以下步骤安装go-torch工具:
1. 安装go-torch
代码语言:javascript复制go get github.com/uber/go-torch
2. 下载并复制flamegraph.pl(可在https://github.com/brendangregg/FlameGraph下载)文件到$GOPATH/bin路径下。
golang通过将性能测试文件输出的方式给出性能测试的结果,我们可以通过go tool查看性能分析文件。
当我们想要对特定代码片段进行性能分析的时候,可以手动调用runtime/pprof下的API进行性能分析,runtime/pprof的API文档:
代码语言:javascript复制https://studygolang.com/pkgdoc
下面我们通过runtime.pprof对一段二维数组操作的代码片段进行性能检测:
首先是对二维数据的两个操作函数:
分别对二维数组进行随机填充,并对二维数组的每一行进行加和操作。
然后编写使用runtime.pprof对目标代码片段的测试代码:
【代码说明】
1. CPUProfile测试代码需要放在待测试代码片段的前面,调用pprof.StartCPUProfile()函数开启pprof测试,我们知道golang的函数在执行完所有的代码之后,会调用函数内部定义的defer函数,所以在此处的defer函数中调用一下pprof.StopCPUProfile()来结束本次测试;
代码语言:javascript复制defer func() { pprof.StopCPUProfile()}()// 也可以简写成defer pprof.StopCPUProfile()
2. 上面代码中,在待测试代码的后面有两段性能信息导出代码(堆栈信息导出、协程信息导出),这两部分的代码负责将pprof测试信息中的指标导出到profile文件中,所以要放在待测试代码片段的后面执行;
3. 上面代码中导出协程信息使用了pprof.Lookup(flagTag)函数,要是想要导出其他的性能flag信息,只需要给pprof.Lookup()函数传入不同的flag,支持的flag可以从下面的文件中查看:
代码语言:javascript复制https://golang.org/src/runtime/pprof/pprof.go
编写好性能测试代码之后,我们就可以对待测试代码进行性能检测了,检测的步骤:
(1)build源代码文件生成可运行的二进制文件:
代码语言:javascript复制go build main.go
可以看到生成了一个可执行的二进制文件:
(2)运行二进制文件生成性能测试文件:
可以看到输出了3个profile文件。
(3)查看profile文件,首先查看一下cpu.prof文件:
代码语言:javascript复制// 命令:go tool pprof <二进制文件名> <要查看的profile文件>go tool pprof main cpu.prof
运行上面的命令之后,就会进入pprof的交互控制台:
使用top命令查看cpu使用情况:
上图中我们可以看到本次此时过程中各个函数执行的情况,比如main函数中的fillMatrix执行的时间占了总执行时间的97.61%,花费了2450ms。
我们还可以通过list funcName来查看指定函数具体的耗时情况:
list fillMatrix命令为我们打印出了fillMatrix函数中耗时最长的是二维数组的赋值。
同样的golang还允许我们以图形化的方式查看函数的耗时情况,使用svg命令可以将所有函数运行情况以svg格式输出:
当我们想要退出pprof工具的时候,可以使用exit命令:
我们可以将该svg图拖拽到浏览器中查看:
上图中红色矩形越大的表示CPU耗时越长,箭头表示整个程序执行的顺序关系。
下面我们使用go-torch查看一下函数执行情况的火炬图,首先使用go-torch命令生成火炬图的:
上面的命令生成了一张torch.svg的图片文件,我们将其拖拽到浏览器中进行查看:
将光标指向不同的火焰柱状图可以查看到不同函数执行是消耗的CPU时间情况。
同样的我们也可以使用命令查看mem.prof中各个函数在执行的过程中对内存的占用情况:
代码语言:javascript复制go tool pprof main mem.prof
上图中我们可以看到二维数据占用了绝大多数的内存。
在性能测试的过程中,有时候我们需要查看一下我们的变量在系统发生GC之后,垃圾变量是否可以被成功的回收,我们可以在输出mem.prof文件内容之前,手动触发一次GC:
按照上面的命令,重新build二进制文件,并运行新的二进制文件,生成mem.prof文件,并进行查看:
我们发现在触发了GC之后,内存占用明显的降低了,从之前的1.49G降低到了1.72M,说明我们声明的变量是GC友好的,可以被系统成功的回收。
上面的测试方式适合我们在开发的过程中对代码片段进行性能检测,而对于线上运行的代码,我们不可能让线上程序一直输出测试文件,这样会影响到线上业务,不过pprof同样提供了线上代码性能测试的方法,运行我们通过http请求对线上任务进行一定时长的采样。
使用pprof对线上业务进行性能采样的步骤和要求:
- 在应用程序中导入 "net/http/pprof"的包,并启动http server
- 通过http://<host>:<port>/debug/pprof访问目标服务器
- 使用下面的命令开始对线上业务进行采样
// pprof进行采样// seconds参数可以指定采样时长,默认采样时间30sgo tool pprof http://<host>:<port>/debug/pprof/profile?seconds=10
// 同样可以使用go-torch进行线上采样生成火焰图go-torch -seconds 10 http://<host>:<port>/debug/pprof/profile
在gin框架中使用pprof进行性能数据采样测试
为了测试pprof和go-torch对线上代码性能的测试,下面首先使用gin框架搭建一个简单的http服务:
该Http服务提供了两个访问接口,/ 和 /fb,后者循环生成多次斐波那契数列。我们直到使用pprof对线上业务进行性能采样测试的时候,必须启动一个golang内置的http server,那么我们通过gin框架也可以满足这个条件吗?我们查看gin的Run(listenPort)源码:
从上面的gin源码可以看到gin的Run函数内部其实也是使用http包进行Http Server的启动。其实不止gin框架,我们常用的beego和iris框架,追踪源码我们会发现,它们也是通过http包启动server服务的,因此当我们使用这些框架进行http server开发,同样可以使用pprof进行线上业务的采样测试。但是在正式使用pprof对gin框架的http server进行测试之前,我们还需要安装gin对pprof的封装包:
代码语言:javascript复制go get github.com/DeanThompson/ginpprof
并在程序中开启ginpprof服务,如上图代码中。
编写完gin的服务器之后,我们通过命令启动http服务:
在浏览器中进行访问:
服务启动成功。
请求连接就可以看到下面的测试网页:
点击就可以查看到不同的测试数据。
我们也可以通过命令行进行性能数据的采样:
上面进行了20s的性能数据采样,当采样结束之后,命令行会自动进入pprof交互模式,这时我们就可以通过命令进行文件查看了:
同样的也可以通过go-torch进行采样分析:
同样的,在指定的采样时间结束之后,go-torch就会结束采样,并将svg的文件输出到命令行文件目录下,我们可以在本地找到这个torch.svg文件,并将其拖拽到浏览器中查看:
在iris框架下使用pprof进行性能数据的采样测试
和gin框架一样,我们首先需要搭建一个iris的http服务(下面使用了v12版本的iris):
和golang内置的http server以及gin框架有所不同,iris下使用pprof工具,我们需要手动添加两个访问接口,添加完上述代码之后,运行iris服务:
代码语言:javascript复制go run main.go
在浏览器中测试访问接口:
服务启动成功。
同样的我们可以通过url来查看pprof的性能数据:
通过命令行来查看pprof采样数据:
代码语言:javascript复制go tool pprof http://127.0.0.1:8081/debug/pprof/heap?seconds=10
在命令行中输入上面的请求,会采样最近10s内线上服务堆栈使用的情况,在输入命令之后,会立即dump并进入到pprof的交互控制台(这和查看profile不太一样):
查看一下iris的middleware/pprof包中的New方法,从源码我们可以看到,iris内部也是使用了golang内置的net/http/pprof进行性能数据的采样,除了profile和heap,我们还可以查看的数据指标也可以从源码中看到:
同样的使用go-torch对性能数据进行采样并输出火焰图:
将火焰图拖拽到浏览器中查看: