性能分析的场景主要有:CPU、Memory、IO、Goroutine、死锁几种。
下面是主要两种类型的应用:
_ "net/http/pprof"
包,专用于采集 web 服务运行数据的分析,即在运行的服务中通过 API 调用取数据。服务
型应用场景中因为应用要一直提供服务,所以 pprof 是通过 API 访问来获取,pprof 使用了默认的
http.DefaultServeMux 挂载这些 API 接口。
package main
import (
"net/http"
_ "net/http/pprof"
)
func main() {
// 服务资源分析时使用
// 访问路径: http://localhost:23300/debug/pprof/
listenAddress := ":23300"
err := http.ListenAndServe(listenAddress, nil)
if err != nil {
panic("启动服务失败")
}
}
"runtime/pprof"
包,专用于采集应用程序运行数据的分析,通过代码手动添加收集命令。工具型应用是一个提
供特定功能使用的工具,使用完就会退出进程的应用,开发者手动控制把 profile 文件保存到报告文件中。封装的
接口可以调用,如要进行 CPU Profiling,则调用 pprof.StartCPUProfile(w io.Writer) 写入到 w 中,停止时调用
StopCPUProfile();要获取内存数据,直接使用 pprof.WriteHeapProfile(w io.Writer) 函数则可。
package main
import (
"fmt"
"math/rand"
"os"
"runtime/pprof"
)
func main() {
// 创建CPU性能分析文件
f, err := os.Create("cpu.prof")
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
// 开始CPU性能分析
if err := pprof.StartCPUProfile(f); err != nil {
fmt.Println(err)
return
}
defer pprof.StopCPUProfile()
// 模拟CPU密集型任务
for i := 0; i < 1000000; i++ {
rand.Intn(100000)
}
}
package main
import (
"fmt"
"math/rand"
"os"
"runtime"
"runtime/pprof"
)
func main() {
f, err := os.Create("mem.pprof")
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
// 这里放需要进行性能分析的代码
for i := 0; i < 1000000; i++ {
rand.Intn(100000)
}
// 手动GC
var ms runtime.MemStats
runtime.GC()
runtime.ReadMemStats(&ms)
err = pprof.WriteHeapProfile(f)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("分析完成,请使用go tool pprof mem.pprof命令进行分析")
}
CPU 分析(profile):每秒采集99次。
内存分配(allocs):堆栈等空间上的内存分配采样,每分配512K字节时取样一次。
阻塞(block):堆栈跟踪导致阻塞的同步原语。
命令行调用(cmdline):命令行调用的程序。
goroutine:当前 goroutine 的堆栈信息。
堆(heap):堆上内存分配的抽样,每分配512K字节时取样一次。
互斥锁(mutex):堆栈跟踪竞争状态互斥锁的持有者。
系统线程的创建(threadcreate):堆栈跟踪系统新线程的创建。
trace:追踪当前程序的执行状况,你可以用 seconds 参数指定抽样持续时间,你获取到 trace 概览后可以用 go
tool trace命令调查这个 trace。
$ curl -o cpu_profile http://127.0.0.1:23300/debug/pprof/profile?seconds=60 || http://127.0.0.1:23300/debug/pprof/heap?seconds=60 ||http://127.0.0.1:23300/debug/pprof/allocs?seconds=60 || http://127.0.0.1:23300/debug/pprof/block || http://127.0.0.1:23300/debug/pprof/groutines?seconds=60
# k8s
$ kubectl cp <ns-name>/<pod_name>:cpu_profile /root/fourier/tmp/cpu_profile
注意:go tool 与生成pprof的go版本应一致,否则可能报错 unrecognized profile format。
$ go tool pprof 文件|链接
$ go tool pprof http://localhost:23300/debug/pprof/goroutine
$ go tool pprof cpu.prof
$ C:\Users\zhangshixing\Desktop\新建文件夹>go tool pprof http://localhost:23300/debug/pprof/goroutine
Fetching profile over HTTP from http://localhost:23300/debug/pprof/goroutine
Saved profile in C:\Users\zhangshixing\pprof\pprof.goroutine.002.pb.gz
Type: goroutine
Time: Feb 5, 2024 at 3:43pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top5
Showing nodes accounting for 5, 100% of 5 total
Showing top 5 nodes out of 37
flat flat% sum% cum cum%
4 80.00% 80.00% 4 80.00% runtime.gopark
1 20.00% 100% 1 20.00% runtime/pprof.runtime_goroutineProfileWithLabels
0 0% 100% 2 40.00% bufio.(*Reader).ReadLine
0 0% 100% 2 40.00% bufio.(*Reader).ReadSlice
0 0% 100% 2 40.00% bufio.(*Reader).fill
flat:在给定函数上运行耗时。
flat%:在给定函数上运行耗时总比例。
sum%:给定函数累计使用 CPU 总比例。
cum:当前函数以及包含子函数的调用运行总耗时。
cum%:同上的 CPU 运行耗时总比例。
最后一列为函数名称,详细见后续指标介绍。
# list [funcName]
(pprof) list Accept
Total: 3
ROUTINE ======================== internal/poll.(*FD).Accept in D:\OwnerSoftwareInstall\Go\src\internal\poll\fd_windows.go
0 1 (flat, cum) 33.33% of Total
. . 971: s, err := sysSocket()
. . 972: if err != nil {
. . 973: return syscall.InvalidHandle, nil, 0, "", err
. . 974: }
. . 975:
. 1 976: errcall, err := fd.acceptOne(s, rawsa[:], o)
. . 977: if err == nil {
. . 978: return s, rawsa[:], uint32(o.rsan), "", nil
. . 979: }
. . 980:
. . 981: // Sometimes we see WSAECONNRESET and ERROR_NETNAME_DELETED is
ROUTINE ======================== net.(*TCPListener).Accept in D:\OwnerSoftwareInstall\Go\src\net\tcpsock.go
0 1 (flat, cum) 33.33% of Total
. . 283:// waits for the next call and returns a generic Conn.
. . 284:func (l *TCPListener) Accept() (Conn, error) {
. . 285: if !l.ok() {
. . 286: return nil, syscall.EINVAL
. . 287: }
. 1 288: c, err := l.accept()
. . 289: if err != nil {
. . 290: return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
. . 291: }
. . 292: return c, nil
. . 293:}
# list `*$ 查看所有函数
$ go tool pprof http://localhost:23300/debug/pprof/heap
# -inuse_space:分析应用程序的常驻内存占用情况
$ go tool pprof -inuse_space http://localhost:23300/debug/pprof/heap
# -alloc_objects: 分析应用程序的内存临时分配情况
$ go tool pprof -alloc_objects http://localhost:23300/debug/pprof/heap
需要安装需安装 Graphviz,下载地址:
https://graphviz.gitlab.io/download/
下载如下安装包:
https://gitlab.com/api/v4/projects/4207231/packages/generic/graphviz-releases/9.0.0/windows_10_cmake_Release_graphviz-install-9.0.0-win64.exe
进入windows命令行界面,输入dot -version
,然后按回车,如果显示graphviz的相关版本信息,则安装配置成
功。
启动:
$ go tool pprof -http=:8080 cpu.prof
访问:http://localhost:8080/ui