1. 先量化再改
凭感觉改性能是瞎调。先压测拿到基线 QPS 和延迟,用 pprof 抓到瓶颈在哪(CPU?分配?锁?),改完再压测对比。第 3 篇的 perf 思路同理。
2. 接 pprof
标准库 net/http/pprof 把剖析接口挂在独立端口,不影响业务路由:
import _ "net/http/pprof" go func() { http.ListenAndServe("127.0.0.1:6060", nil) // 独立 debug 端口 }()
_ 导入自动注册 /debug/pprof/ 下的一系列路由。别挂到对外端口,只在调试环境开。
3. 抓 CPU profile
go tool pprof http://127.0.0.1:6060/debug/pprof/profile?seconds=30
采 30 秒 CPU 样本,进交互界面 top 看耗时函数,web 出火焰图。热点函数一目了然。
4. 抓内存和协程
go tool pprof http://127.0.0.1:6060/debug/pprof/heap # 内存分配 go tool pprof http://127.0.0.1:6060/debug/pprof/goroutine # 协程数
内存涨不停看 heap,协程泄漏看 goroutine(没退出的协程堆积)。
5. 压测拿基线
echo "GET http://127.0.0.1:8080/articles/1" | vegeta attack -rate 1000 -duration 30s | vegeta report
rate 是每秒请求数,report 出延迟分布和成功率。先低压再加压,找到拐点 QPS。
6. 常见瓶颈
- 锁竞争:
sync.Mutex粒度太大,pprof 的mutexprofile 能看到。 - 频繁分配:热路径里反复
make/string拼接,堆压力推高 GC。用sync.Pool或预分配。 - N+1 查询:第 1 篇讲过的,一次请求打 N 次库,用 dataloader 或批量查。
- 没设 context 超时:下游慢把连接/协程占满(第 3 篇)。
7. 上线清单
改性能前先压测基线,改完对比,别盲调。pprof 挂独立 debug 端口,生产按需开、不对外。CPU 看热点函数,内存看 heap,协程泄漏看 goroutine 数。定位到具体函数再改,改一处压一次。把 pprof 地址和压测脚本留进运维文档,下次有人能复现。
下一篇讲数据库进阶,连接池和读写分离是上线必踩的。