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 从触发到数据落盘的核心路径:

CPU & 内存 Profiling 工作流程 HTTP 请求 / 代码调用 runtime/pprof 包 net/http/pprof 包 CPU:SIGPROF 信号采样 内存:malloc 分配采样 runtime 记录调用栈 + 采样计数 写入 pprof 二进制文件 HTTP 响应返回 pprof 数据

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

  1. 确认导入:import _ "net/http/pprof" 是否已添加?
  2. 确认端口:http.ListenAndServe(":6060", nil) 是否在独立 goroutine 中启动?
  3. 确认防火墙:6060 端口是否对内网开放?生产环境建议绑定 127.0.0.1:6060。
  4. 确认采样时长:CPU 采样建议 30 秒以上,内存快照无需时长参数。
  5. 确认数据可达:curl http://localhost:6060/debug/pprof/ 能否返回 HTML 页面?
生产安全提示: 切勿将 pprof 端口暴露到公网。建议通过内网反向代理或使用 pprof 的 http.ListenAndServe 绑定本地回环地址,再通过 SSH 隧道或 Kubernetes port-forward 访问。