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/