概念定义
事故时才挂的剖析器只看得见事故。持续剖析保有对一切的低分辨率记录,“这个分配点何时出现、当时改了什么”从此有答案。
工作机制
采样剖析器按固定间隔中断线程记录栈快照;eBPF 变体无需应用插桩即可抓内核与用户栈。样本聚合成按服务按版本带时间窗的剖析;跨部署 diff 剖析浮出新热点,分配剖析在 OOM 之前给出泄漏候选。
使用场景
反复出现性能谜团的生产服务、部署频繁到“哪个变更干的”成为日常之问的舰队、以及任何被期望在生产直接排障而非本地复现的平台。
常见误区
剖析不只在事故响应用武:连续剖析的价值恰恰在事故之前——以部署间剖析 diff 捕回归。统计采样在百赫兹级的开销只有个位数百分比 CPU,会失真的是插桩式剖析器。剖析也不能替代 trace:trace 把延迟归因到跨服务,剖析把消耗归因到进程内——生产排障两者都要。剖析基数与指标同构:按实例打标签会爆存储,聚到有用维度并限制原始样本;语言运行时不同开销预算也不同,先在最慢路径验证再全网铺开。
代码示例
package main
import (
"fmt"
"log"
"net/http"
"net/http/pprof"
"runtime"
"time"
)
func busyWork() {
total := 0
for i := 0; i < 2_000_000; i++ {
total += i % 7
}
_ = total
}
func setupProfiler(addr string) *http.Server {
mux := http.NewServeMux()
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
mux.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
server := &http.Server{Addr: addr, Handler: mux}
go func() {
log.Printf("pprof listening on %s", addr)
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Printf("pprof server error: %v", err)
}
}()
return server
}
func main() {
prof := setupProfiler("127.0.0.1:6060") // 仅内网可达,不暴露公网
defer prof.Close()
runtime.SetMutexProfileFraction(5)
runtime.SetBlockProfileRate(200)
for i := 0; i < 3; i++ {
busyWork()
time.Sleep(100 * time.Millisecond)
}
// 使用方式:
// go tool pprof http://127.0.0.1:6060/debug/pprof/profile?seconds=10
// go tool pprof http://127.0.0.1:6060/debug/pprof/heap
fmt.Println("profile samples collected")
}