beego.pprofon = true 无效是因为它仅触发 net/http/pprof.init() 向 http.defaultservemux 注册路由,而 beego 使用独立路由系统,不使用 defaultservemux,导致 /debug/pprof/ 路径无 handler 处理,必然返回 404;必须手动启动独立 pprof server(如 :6060)并确保在 beego.run() 前调用 http.listenandserve(":6060", nil)。

Beego 项目里直接设 beego.PprofOn = true 基本无效——它只开个空路由,不注册 handler,访问 /debug/pprof/ 必然 404。
为什么 beego.PprofOn = true 不起作用
这个配置项只是让 Beego 启动时调用一次 net/http/pprof 的 init 函数,但该函数只向 http.DefaultServeMux 注册路由;而 Beego 使用自己的 ControllerRouter 和 HttpServer,完全不走 DefaultServeMux。所以即使开了,/debug/pprof/ 路径根本没被任何 handler 拦截。
常见错误现象:curl http://localhost:8080/debug/pprof/ 返回 404,或者浏览器打开后空白/重定向失败。
- 别依赖配置开关,必须手动桥接 HTTP handler 到 Beego 路由系统
- 不能只写
import _ "net/http/pprof"就以为万事大吉 - Beego v2.x 默认监听端口是
:8080,pprof 单独开:6060或:8888是最稳妥的,避免路由冲突
正确启动 pprof HTTP 服务(推荐独立端口)
在 main.go 的 main() 函数开头加一段协程监听,不干扰主服务逻辑:
go func() {
log.Println("Starting pprof server on :6060")
if err := http.ListenAndServe(":6060", nil); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()
注意:http.ListenAndServe(":6060", nil) 的第二个参数为 nil,才能触发 http.DefaultServeMux 被使用——这是 net/http/pprof 注册成功的前提。
- 必须在
beego.Run()之前启动,否则主线程阻塞,协程可能无法调度 - 端口别选 8080,避免和 Beego 主服务端口撞车;6060 是 pprof 事实标准端口
- 不需要任何 Beego 配置或中间件,纯标准 net/http 启动即可
采集 CPU profile 时 top 全是 runtime.futex 怎么办
这不是 pprof 坏了,而是你在采样时业务根本没跑起来:goroutine 都卡在锁、channel receive、数据库等待或 GC 上,CPU 时间没花在你的函数里。
真实压测前务必确认两点:
- 先用
wrk -c 10 -t 2 -d 10s http://localhost:8080/your/api确保接口能稳定返回,再开 pprof 采样 - 采样时间至少 30 秒:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 - 如果 top 还是看不到业务函数名,立刻切到
/debug/pprof/goroutine?debug=2,搜索semacquire、chan receive、重复出现的models.UserLogin行号——那才是阻塞源头
分析 heap profile 要加 ?gc=1 参数
/debug/pprof/heap 默认返回的是「已分配但尚未复用」的内存快照,不是真实泄漏指标。不加 ?gc=1,inuse_space 涨得再高也没意义。
正确做法:
- 对比两次采样:
curl "http://localhost:6060/debug/pprof/heap?gc=1" -o heap1.pb.gz - 压测 2 分钟后,再采一次:
curl "http://localhost:6060/debug/pprof/heap?gc=1" -o heap2.pb.gz - 用
go tool pprof -base heap1.pb.gz heap2.pb.gz查增长部分,重点关注你自己的 struct 类型(如*models.User)
最容易被忽略的一点:Beego 的 c.Data["json"] = data 会触发 json.Marshal,若 data 是含循环引用或嵌套过深的 struct,encoding/json 会持续分配临时 []byte,这在 heap profile 里体现为大量 []uint8 ——不是数据库问题,是序列化姿势不对。











