必须先启动 HTTP 服务,否则 go tool pprof 无法连接;默认 CPU profile 仅通过 HTTP 暴露,未启动服务时执行会报“connection refused”错误,需在 main 中导入 _ "net/http/pprof" 并用 goroutine 启动 ListenAndServe,且避免提前退出。

必须先启动 HTTP 服务,否则 go tool pprof 连不上
pprof 的 CPU profile 数据默认只通过 HTTP 接口暴露,不启动服务就执行 go tool pprof http://localhost:6060/debug/pprof/profile,必然报错:Get "http://localhost:6060/debug/pprof/profile": dial tcp [::1]:6060: connect: connection refused。
实操建议:
- 在
main()中 import"net/http"和_ "net/http/pprof"(下划线导入会自动注册/debug/pprof/路由) - 用 goroutine 启动 HTTP server,例如:
go http.ListenAndServe(":6060", nil),端口避开主服务端口 - 确保该 goroutine 不退出——别写成启动后立刻 return,否则服务秒关,profile 接口不可达
go tool pprof 默认只采 30 秒,容易错过真实热点
线上偶发的 CPU 高峰(比如某次大请求触发的密集计算)往往持续很短。默认 30 秒采集很可能全程“空跑”,profile 里全是调度器开销或系统调用,根本看不到业务函数。
实操建议:
- 手动延长采集时长:
go tool pprof -seconds=60 http://localhost:6060/debug/pprof/profile - 压测中精准触发:用
curl -s "http://localhost:6060/debug/pprof/profile?seconds=30"在请求打进来前瞬间发起 - 避免在本地开发机做全量 profile——低负载下采样噪声大,结果失真;优先选压测环境或灰度实例
top 只显示函数名,真正要盯的是调用栈上下文
进入 pprof 交互模式后敲 top,输出类似:12.34s 47.83% github.com/xxx/pkg.(*Processor).DoWork。这只能说明 DoWork 消耗最多,但无法判断是它自己慢,还是它调用的 json.Marshal 或正则匹配拖垮了整条链路。
实操建议:
- 用
top -cum查看累计耗时,确认是否真由该函数主导(cum包含所有子调用,flat仅本函数) - 用
web或svg生成火焰图,一眼识别深层调用中的“尖刺” - 用
list DoWork查看函数内具体哪几行耗时高——前提是编译时加了go build -gcflags="all=-N -l"保留调试信息
程序内嵌式采集比 HTTP 方式更可控,但要注意文件写入时机
对非 HTTP 服务(如 CLI 工具、批处理任务),或需精确控制起止点的场景,直接用 runtime/pprof.StartCPUProfile 更合适。但常见错误是:文件没 close、profile 没 stop、或写入路径无权限,导致 cpu.prof 为空或损坏。
实操建议:
- 用
os.Create("cpu.prof")创建文件,并defer f.Close() -
pprof.StartCPUProfile(f)后务必配对defer pprof.StopCPUProfile() - 确保采集逻辑包裹在实际工作代码前后,而非放在
main()结尾——否则 profile 早停了,业务还没跑
真正难的不是拿到 profile 文件,而是区分 flat 和 cum、看懂火焰图里哪一层才是瓶颈源头。很多团队花一小时采数据,却在 top 输出里反复猜“是不是这个函数的问题”,其实只要多敲两行命令,就能跳过 80% 的误判。











