golang编写的程序在开发过程、运行过程中可能会出现一些意想不到的问题,诸如:cpu暴涨、内存吃紧、接口响应时间过长、goroutine数量暴涨等等问题,这个时候就涉及到性能分析和问题定位排查。
pprofprofile.protoprofile.protoProtobuf v3callstacksymbolization
pprof
runtime/pprofnet/http/pprofgo test
通过pprof这个工具也就是使用go官方提供的两个包可得到一些分析报告或执行一些分析:
- 生成报告:即生成一个后续可用来分析的性能分析报告文件
- 交互式终端里直接输入命令查看各项指标
- web界面报告:即打开一个web网页,在页面里点点点就可以看到各种图形化一目了然的指标
pprof的分析报告可以为我们提供如下类型的分析指标<这段是摘除>:
runtime.SetBlockProfileRateruntime.SetMutexProfileFraction
http服务的pprof
go原生提供http便捷服务开发包,一个典型的http服务结构如下:
package main import ( "net/http" "time" _ "net/http/pprof" ) func main() { srv := &http.Server{ Addr: ":9080", Handler: nil, ReadTimeout: 60 * time.Second, WriteTimeout: 65 * time.Second, MaxHeaderBytes: 1 << 20, //1MB } http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) { _, _ = writer.Write([]byte("hello world!")) }) _ = srv.ListenAndServe() }
_ "net/http/pprof"initinit
func init() { http.HandleFunc("/debug/pprof/", Index) http.HandleFunc("/debug/pprof/cmdline", Cmdline) http.HandleFunc("/debug/pprof/profile", Profile) http.HandleFunc("/debug/pprof/symbol", Symbol) http.HandleFunc("/debug/pprof/trace", Trace) }
net/http/pprof
一些指标的说明<这段是摘除>:
$HOST/debug/pprof/allocs$HOST/debug/pprof/block$HOST/debug/pprof/goroutine$HOST/debug/pprof/heap$HOST/debug/pprof/mutex$HOST/debug/pprof/profile$HOST/debug/pprof/threadcreate
可视化图形分析
graphvizgraphvizCould not execute dot; may need to install graphviz.
1、获取profile文件
启动已加载pprof的服务,终端里通过wget命令获取profile文件:
wget http://127.0.0.1:9080/debug/pprof/profile\?seconds\=20
secondsWriteTimeoutprofile duration exceeds server's WriteTimeoutsecondsWriteTimeoutsecondsWriteTimeout
上述wget命令会阻塞采样,阻塞采样期间如果要查看某个接口的采样分析结果,这个时候需要去访问这个接口以便执行这个接口相关的逻辑。
2、生成可视化网页和图
profile
go tool pprof -http=:6001 profile
以及所谓的火焰图
自定义抓取profile
net/http/pprofProfileruntime/pprofnet/http/pprofruntime/pprofruntime/pprof
// Profile responds with the pprof-formatted cpu profile. // Profiling lasts for duration specified in seconds GET parameter, or for 30 seconds if not specified. // The package initialization registers it as /debug/pprof/profile. func Profile(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Content-Type-Options", "nosniff") sec, err := strconv.ParseInt(r.FormValue("seconds"), 10, 64) if sec <= 0 || err != nil { sec = 30 } if durationExceedsWriteTimeout(r, float64(sec)) { serveError(w, http.StatusBadRequest, "profile duration exceeds server's WriteTimeout") return } // Set Content Type assuming StartCPUProfile will work, // because if it does it starts writing. w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", `attachment; filename="profile"`) if err := pprof.StartCPUProfile(w); err != nil { // StartCPUProfile failed, so no writes yet. serveError(w, http.StatusInternalServerError, fmt.Sprintf("Could not enable CPU profiling: %s", err)) return } sleep(r, time.Duration(sec)*time.Second) pprof.StopCPUProfile() }
runtime/pprof
// 启动cpu采样,参数为一个io.Writer接口,通过参数可以指定将采样写到何处,譬如:文件 pprof.StartCPUProfile(writer) // 采样开始执行后会启动一个协程去持续的采样获取样本数据 // 故而当前协程需要暂停等待协程去执行采样 time.sleep(t) // 采样结束调用停止方法,本质是通过chan传递一个消息告诉上述采样协程需要停止 pprof.StopCPUProfile()
---
参考资料:
① https://golang2.eddycjy.com/posts/ch6/01-pprof-1/