Go 提供了一些优秀的诊断工具来帮助我们深入分析应用程序的执行情况。
这篇文章核心关注点是:分析和执行跟踪器。
这两个工具都非常重要,它们应该成为任何对优化感兴趣的 Go 开发人员的核心工具集的一部分。
首先,我们来讨论下性能分析。
性能分析 Profiling
分析工具提供了对应用程序执行的洞察力。它使我们能够解决性能问题、检测竞争、定位内存泄漏等。
这些信息可以通过几个分析工具来收集:
CPU— 确定应用程序的时间花在了哪里
Goroutine— 报告正在运行的 goroutines 堆栈跟踪
Heap— 报告堆内存分配以监视当前内存使用情况并检查可能的内存泄漏
Mutex— 报告锁争情况来分析代码中互斥锁使用行为以及应用程序是否在锁定调用上花费了太多时间
Block— 显示 goroutines 阻塞等待同步原语的位置
性能分析是通过 分析器(profiler) 工具进行检测来实现的,在 Go 中使用称为 pprof。
首先,让我们了解如何和何时启用 pprof,然后再讨论最关键的配置分析类型。
开启 pprof
有几种方法可以启用 pprof。例如,我们可以使用 net/http/pprof 包通过 HTTP 提供分析数据:
package mainimport ( "fmt" "log" "net/http" _ "net/http/pprof" // Blank import to pprof)func main() { // Exposes an HTTP endpoint http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "") }) log.Fatal(http.ListenAndServe(":80", nil))}
导入 net/http/pprof 的作用是,我们可以通过 http://host/debug/pprof 来访问 pprof。
请注意,即使在生产环境中启用 pprof 也是安全的 (https://go.dev/doc/diagnostics#profiling)。影响性能的分析,比如 CPU 分析,默认情况下是不启用的,也不会连续运行。它们仅在特定时间段内被激活。
现在我们已经了解了如何公开 pprof 访问路由,接下来讨论最常见的几种分析。
CPU 分析
CPU 分析器依赖于操作系统和信号。
当它被激活时,应用程序默认通过 SIGPROF 信号要求操作系统每 10 毫秒中断一次。当应用程序收到 SIGPROF 时,它会暂停当前活动并将执行转移到分析器。
分析器收集诸如当前 goroutine 活动之类的数据,并汇总可以检索的执行统计信息;然后停止分析并继续执行直到下一次的 SIGPROF。
我们可以访问 /debug/pprof/profile 路由来激活 CPU 分析。默认情况下,访问此路由会执行 30 秒的 CPU 分析。在 30 秒内,我们的应用程序每 10 毫秒中断一次。
请注意,可以更改这两个默认值:使用 seconds 参数将分析应该持续多长时间传递给路由(例如 /debug/pprof/profile?seconds=15),也可以更改中断率(甚至小于 10 毫秒)。
但多数情况下,10 毫秒应该足够了,在减小这个值(意味着增加频率)时,我们应该注意不要对性能产生影响。30 秒后,就可以下载 CPU 分析器的结果。
注意:
也可以通过 -cpuprofile 标志来开启 CPU 分析器,比如在运行基准测试时就可以用这种方式。
例如,执行以下命令后可通过 /debug/pprof/profile 下载到相同的分析结果文件。
$ go test -bench=. -cpuprofile profile.out
从这个文件,我们可以使用 go tool 来查看结果分析:
$ go tool pprof -http=:8080 <file>
此命令会打开一个显示调用图的 Web UI。
图 1 显示了一个示例。箭头越大,这条分支就越是热路径。通过分析此图表就可以进一步分析程序的执行情况。
图1 程序在30秒内的调用图
例如,图2 中的图表告诉我们,在 30 秒内,*FetchResponse