pprof HTTP服务严禁暴露公网,须用独立监听地址+网络策略隔离;采样需按需开启并动态控制,优先离线分析多时间点profile以准确定位瓶颈。

pprof HTTP服务不能直接暴露在公网
生产环境开 net/http/pprof 默认路由等于把内存、goroutine、CPU采样数据白送给任何人。常见错误是只加了反向代理或简单 Basic Auth,但没关掉调试端口或没限制来源 IP。
- 默认
/debug/pprof/路由不校验身份,只要能连上端口就能下载 profile 数据 - 某些云环境(如 Kubernetes Pod)里,Service 或 Ingress 暴露了
:6060且没网络策略约束,风险极高 -
runtime.SetMutexProfileFraction和runtime.SetBlockProfileRate开启后可能带来可观性能开销,尤其在高并发服务中
用独立监听地址 + 网络层隔离代替“加个中间件”
别试图在主 HTTP 路由里用中间件拦 /debug/pprof —— pprof 是直接注册到 http.DefaultServeMux 的,中间件根本收不到请求。正确做法是拆开监听。
- 启动一个单独的
http.Server,绑定到仅内网可达的地址,比如127.0.0.1:6060或运维专用子网10.100.0.5:6060 - 确保宿主机防火墙、K8s NetworkPolicy、云安全组都禁止外部访问该端口
- 如果必须走主服务域名访问,用反向代理(如 Nginx)做路径重写 + IP 白名单,而不是把 pprof mux 挂进主路由
示例片段:
go
pprofMux := http.NewServeMux()
pprofMux.HandleFunc("/debug/pprof/", http.HandlerFunc(pprof.Index))
// 注意:不要用 http.DefaultServeMux
go func() {
log.Println(http.ListenAndServe("127.0.0.1:6060", pprofMux))
}()
采样开关要按需动态控制,别全量常开
CPU profile 默认关闭,但 heap、goroutine、mutex、block 这些是运行时自动采集的,不设限就一直攒着——尤其 block 和 mutex 在锁竞争激烈时会显著拖慢服务。
立即学习“go语言免费学习笔记(深入)”;
- 上线前确认
runtime.SetBlockProfileRate(0)和runtime.SetMutexProfileFraction(0),需要时再临时打开 - heap profile 每次 GC 都 dump,对内存紧张的服务很危险;可用
runtime.GC()触发一次后立即抓取,而非长期开启 - 用
pprof.Lookup("goroutine").WriteTo(w, 1)抓 goroutine stack 时,传1表示带栈帧,2表示带完整调用链,后者更重,别在线上默认用2
离线分析比在线看更安全也更准
线上 curl http://localhost:6060/debug/pprof/profile?seconds=30 看 CPU profile 很方便,但容易受瞬时抖动干扰,且 30 秒采样期间服务本身负载就变了。
- 优先用
curl -s "http://localhost:6060/debug/pprof/heap" > heap.pb.gz下载二进制 profile,本地用go tool pprof分析 - 避免在生产机上跑
go tool pprof -http=:8080 heap.pb.gz,这会起 Web 服务,又多一个攻击面 - 多个 profile 时间点对比(比如高峰前后各抓一次 heap),比单次结果更有价值;注意用
pprof -base做 diff
真正难的不是怎么开 pprof,是怎么判断哪类 profile 正确反映了瓶颈——比如 goroutine 数暴涨,可能是泄漏,也可能是短时批量任务,得结合 pprof -symbolize=remote 看符号还原后的实际调用路径,而不是只数 goroutine 总数。











