使用
一、代码中嵌入pprof的路由
使用kratos框架
- 没有路由的前缀
srv.Handle("/", pprof.NewHandler())
- 有路由前缀,需要自己封装一下,我封装了一个
pprofRouter
函数
package server
import (
"github.com/cctip/campaign-pkg/http/codec"
"github.com/cctip/campaign-pkg/http/codecv2"
"github.com/cctip/campaign-pkg/middleware/auth"
"github.com/cctip/campaign-toolkit/internal/conf"
"github.com/cctip/campaign-toolkit/internal/pkg/wrapper"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/transport/http"
"net/http/pprof"
)
// NewHTTPServer new an HTTP server.
func NewHTTPServer(c *conf.Server, logger log.Logger) *http.Server {
var opts = []http.ServerOption{
http.PathPrefix("/test/v1"),
http.ErrorEncoder(codecv2.StandardErrorEncoder(wrapper.Failure)),
http.ResponseEncoder(codec.StandardResponseEncoder(wrapper.Success)),
http.Filter(auth.Wrapper),
}
if c.Http.Network != "" {
opts = append(opts, http.Network(c.Http.Network))
}
if c.Http.Addr != "" {
opts = append(opts, http.Address(c.Http.Addr))
}
if c.Http.Timeout != nil {
opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
}
srv := http.NewServer(opts...)
//pprof
pprofRouter(srv)
return srv
}
func pprofRouter(srv *http.Server) {
route := srv.Route("/debug/pprof/")
route.GET("/", WrapF(pprof.Index))
route.GET("/cmdline", WrapF(pprof.Cmdline))
route.GET("/profile", WrapF(pprof.Profile))
route.POST("/symbol", WrapF(pprof.Symbol))
route.GET("/trace", WrapF(pprof.Symbol))
route.GET("/allocs", WrapF(pprof.Handler("allocs").ServeHTTP))
route.GET("/block", WrapF(pprof.Handler("block").ServeHTTP))
route.GET("/goroutine", WrapF(pprof.Handler("goroutine").ServeHTTP))
route.GET("/heap", WrapF(pprof.Handler("heap").ServeHTTP))
route.GET("/mutex", WrapF(pprof.Handler("mutex").ServeHTTP))
route.GET("/threadcreate", WrapF(pprof.Handler("threadcreate").ServeHTTP))
}
func WrapF(handle func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
return func(ctx http.Context) error {
handle(ctx.Response(), ctx.Request())
return nil
}
}
因为pprof路由是注册在其他server结构上的,要在我们的前缀Serve结构上去访问ServeMux注册的hander,我们需要嵌入一下ServeMux注册的hander。
二、控制面板查看
完成路由注册,就可以通过页面访问pprof的控制面板
#有前缀
http://127.0.0.1:10000/test/v1/debug/pprof
#无前缀
#http://127.0.0.1:10000/debug/pprof
三、工具查看
- 交互页面(命令行)
#当前的常驻内存使用情况
go tool pprof -inuse_space http://127.0.0.1:10002/debug/pprof/heap
#cpu占用使用情况(默认60s)
#go tool pprof -inuse_space http://127.0.0.1:10002/debug/pprof/profile
Fetching profile over HTTP from http://127.0.0.1:10002/debug/pprof/profile
Saved profile in /Users/darrendu/pprof/pprof.___7go_build_github_com_cctip_campaign_toolkit_cmd_campaign_toolkit_dev.samples.cpu.003.pb.gz
Entering interactive mode (type "help" for commands, "o" for options)
#查看top (默认10)
(pprof) % top
#查看top (查看图)
(pprof) % web
#定位代码位置
(pprof) %。list 函数名
- 使用graphviz
我们可以在交互页面找到gz文件
执行命令
go tool pprof -http localhost:6002 /Users/darrendu/pprof/pprof.___7go_build_github_com_cctip_campaign_toolkit_cmd_campaign_toolkit_dev.samples.cpu.003.pb.gz
go tool pprof -http localhost:6002 XX.gz文件绝对路径
可以查看view去查看图的展示类型
四、排查方式
- 平常是先查看火焰图找到
占比最大的方块
,我们可以查看到最大方块
的下方其他子方块,为最大方块的调用。
- 我们然后通过切换view去查看graph,我们去找到最大方块的调用链,就可以去定位代码了。
总结
为什么使用pprof,只能说写了bug,bug比较低级就不展示排查流程了😑😑~;
当遇见性能问题时,我们可以通过pprof生成gz文件然后通过graghviz
工具去可视化性能数据,然后更好的定位到bug位置。