3. pprof入门:runtime/pprof与net/http/pprof包,开启CPU与内存Profiling
性能调优的第一步是可观测。Go 标准库提供了两套 Profiling 入口:runtime/pprof 适用于短生命周期工具或一次性采样,net/http/pprof 适用于长期运行的后端服务。本章将带你掌握这两种方式,并理解 CPU 与内存 Profiling 的核心原理。
3.1 两种 Profiling 入口对比
| 对比维度 | runtime/pprof | net/http/pprof |
|---|---|---|
| 适用场景 | CLI 工具、一次性任务、Benchmark | HTTP 服务、长期运行的后端 |
| 启动方式 | 手动调用 pprof.StartCPUProfile() / pprof.WriteHeapProfile() |
import 后自动注册路由到 /debug/pprof/ |
| 采样控制 | 代码内显式控制开始/停止 | 通过 HTTP 请求触发采样 |
| 依赖注入 | 无额外依赖 | 需引入 net/http/pprof 包 |
| 典型命令 | go tool pprof [binary] [profile] |
go tool pprof http://localhost:6060/debug/pprof/profile |
选型建议: 99% 的线上服务场景应优先使用
net/http/pprof,它无需修改业务逻辑即可按需采样;runtime/pprof 更适合离线分析或无法暴露 HTTP 端口的场景。
3.2 CPU Profiling:采样原理与开启方式
Go 的 CPU Profiler 基于 定时信号采样:操作系统每隔 10ms(默认)向进程发送 SIGPROF 信号,Go runtime 在信号处理函数中记录当前 goroutine 的调用栈。采样频率可通过 runtime.SetCPUProfileRate() 调整,但官方推荐保持默认。
3.2.1 使用 runtime/pprof 进行 CPU 采样
package main
import (
"os"
"runtime/pprof"
)
func main() {
f, _ := os.Create("cpu.pprof")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
// 业务逻辑
heavyComputation()
}
func heavyComputation() {
sum := 0
for i := 0; i < 1e8; i++ {
sum += i
}
}
3.2.2 使用 net/http/pprof 进行 CPU 采样
package main
import (
"log"
"net/http"
_ "net/http/pprof" // 匿名导入,自动注册路由
)
func main() {
go func() {
log.Println(http.ListenAndServe(":6060", nil))
}()
// 业务逻辑
select {}
}
启动后,通过以下命令采集 30 秒 CPU 样本:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
关键参数:
seconds 控制采样时长,生产环境建议 30~60 秒;过短会导致样本不足,过长可能影响服务性能。
3.3 内存 Profiling:堆快照与分配追踪
内存 Profiling 记录的是堆内存分配的调用栈,而非实时内存使用量。Go runtime 在每次 malloc 时按概率(默认每 512KB 分配采样一次)记录栈信息。采样率可通过 runtime.MemProfileRate 调整。
3.3.1 使用 runtime/pprof 获取堆快照
func writeHeapProfile() {
f, _ := os.Create("heap.pprof")
defer f.Close()
pprof.WriteHeapProfile(f)
}
3.3.2 使用 net/http/pprof 获取堆快照
# 获取当前堆快照
go tool pprof http://localhost:6060/debug/pprof/heap
# 获取分配历史(-alloc_space 显示累计分配量)
go tool pprof -alloc_space http://localhost:6060/debug/pprof/heap
注意: 默认
/debug/pprof/heap 返回的是 inuse_space(当前存活对象),排查内存泄漏时需关注 -alloc_space 和 -alloc_objects 模式。
3.4 CPU 与内存 Profiling 工作流程
下图展示了两种 Profiling 从触发到数据落盘的核心路径:
3.5 常用 pprof 端点速查表
| 端点 | 类型 | 说明 | 常用参数 |
|---|---|---|---|
/debug/pprof/profile |
CPU | CPU 采样,默认 30 秒 | ?seconds=60 |
/debug/pprof/heap |
内存 | 堆内存快照(inuse) | ?gc=1 强制 GC 后采样 |
/debug/pprof/goroutine |
Goroutine | 所有 goroutine 调用栈 | ?debug=2 输出完整栈信息 |
/debug/pprof/block |
阻塞 | 同步原语阻塞事件 | 需先启用 runtime.SetBlockProfileRate() |
/debug/pprof/mutex |
互斥锁 | 锁竞争事件 | 需先启用 runtime.SetMutexProfileFraction() |
3.6 快速验证:开启 Profiling 的 Checklist
- 确认导入:
import _ "net/http/pprof"是否已添加? - 确认端口:
http.ListenAndServe(":6060", nil)是否在独立 goroutine 中启动? - 确认防火墙:6060 端口是否对内网开放?生产环境建议绑定
127.0.0.1:6060。 - 确认采样时长:CPU 采样建议 30 秒以上,内存快照无需时长参数。
- 确认数据可达:
curl http://localhost:6060/debug/pprof/能否返回 HTML 页面?
生产安全提示: 切勿将 pprof 端口暴露到公网。建议通过内网反向代理或使用
pprof 的 http.ListenAndServe 绑定本地回环地址,再通过 SSH 隧道或 Kubernetes port-forward 访问。