本文内容纲要:

- Profiling

- 两种收集方式

- 工具型应用

- 服务型应用

- go tool pprof 命令:获取和分析 Profiling 数据

- CPU Profiling

- Memory Profiling

- go-torch 和火焰图

- 和测试工具的集成

Profiling

在计算机性能调试领域里,profiling 就是对应用的画像,这里画像就是应用使用 CPU 和内存的情况。也就是说应用使用了多少 CPU 资源?都是哪些部分在使用?每个函数使用的比例是多少?有哪些函数在等待 CPU 资源?知道了这些,我们就能对应用进行规划,也能快速定位性能瓶颈。

在 go 语言中,主要关注的应用运行情况主要包括以下几种:

  • CPU profile:报告程序的 CPU 使用情况,按照一定频率去采集应用程序在 CPU 和寄存器上面的数据
  • Memory Profile(Heap Profile):报告程序的内存使用情况
  • Block Profiling:报告 goroutines 不在运行状态的情况,可以用来分析和查找死锁等性能瓶颈
  • Goroutine Profiling:报告 goroutines 的使用情况,有哪些 goroutine,它们的调用关系是怎样的

两种收集方式

runtime/pprofnet/http/pprof

工具型应用

runtime/pprof
pprofpprof.StartCPUProfile()w io.WriterStopCPUProfile()
main.go
f, err := os.Create(*cpuprofile)
...
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()

应用执行结束后,就会生成一个文件,保存了我们的 CPU profiling 数据。

WriteHeapProfilestartstop
f, err := os.Create(*memprofile)
pprof.WriteHeapProfile(f)
f.Close()

服务型应用

net/http/pprof
http.DefaultServeMuxhttp.ListenAndServe("0.0.0.0:8000", nil)
import _ "net/http/pprof"
Mux
r.HandleFunc("/debug/pprof/", pprof.Index)
r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
r.HandleFunc("/debug/pprof/profile", pprof.Profile) r.HandleFunc("/debug/pprof/symbol", pprof.Symbol) r.HandleFunc("/debug/pprof/trace", pprof.Trace)
/debug/pprof
/debug/pprof/

profiles:
0    block
62    goroutine
444    heap
30 threadcreate full goroutine stack dump

这个路径下还有几个子页面:

/debug/pprof/profile/debug/pprof/heap/debug/pprof/block/debug/pprof/goroutines

go tool pprof 命令:获取和分析 Profiling 数据

go tool pprof
graphviz
$ sudo apt-get install -y graphviz

NOTE:获取的 Profiling 数据是动态的,要想获得有效的数据,请保证应用处于较大的负载(比如正在生成中运行的服务,或者通过其他工具模拟访问压力)。否则如果应用处于空闲状态,得到的结果可能没有任何意义。

CPU Profiling

go tool pprofgo tool pprof [binary] [source]binarysource
➜  go tool pprof ./hyperkube http://172.16.3.232:10251/debug/pprof/profile
Fetching profile from http://172.16.3.232:10251/debug/pprof/profile Please wait... (30s) Saved profile in /home/cizixs/pprof/pprof.hyperkube.172.16.3.232:10251.samples.cpu.002.pb.gz Entering interactive mode (type "help" for commands) (pprof)
?seconds=60help
topN
(pprof) top10
130ms of 360ms total (36.11%)
Showing top 10 nodes out of 180 (cum >= 10ms) flat flat% sum% cum cum% 20ms 5.56% 5.56% 100ms 27.78% encoding/json.(*decodeState).object 20ms 5.56% 11.11% 20ms 5.56% runtime.(*mspan).refillAllocCache 20ms 5.56% 16.67% 20ms 5.56% runtime.futex 10ms 2.78% 19.44% 10ms 2.78% encoding/json.(*decodeState).literalStore 10ms 2.78% 22.22% 10ms 2.78% encoding/json.(*decodeState).scanWhile 10ms 2.78% 25.00% 40ms 11.11% encoding/json.checkValid 10ms 2.78% 27.78% 10ms 2.78% encoding/json.simpleLetterEqualFold 10ms 2.78% 30.56% 10ms 2.78% encoding/json.stateBeginValue 10ms 2.78% 33.33% 10ms 2.78% encoding/json.stateEndValue 10ms 2.78% 36.11% 10ms 2.78% encoding/json.stateInString
累加值 cumulative
toplistdisasm
webwebsvg

这个调用图包含了更多的信息,而且可视化的图像能让我们更清楚地理解整个应用程序的全貌。图中每个方框对应一个函数,方框越大代表执行的时间越久(包括它调用的子函数执行时间,但并不是正比的关系);方框之间的箭头代表着调用关系,箭头上的数字代表被调用函数的执行时间。

encoding/json.(*decodeState).object
pproflist
(pprof) list podFitsOnNode
Total: 120ms
ROUTINE ======================== k8s.io/kubernetes/plugin/pkg/scheduler.podFitsOnNode in /home/cizixs/go/src/k8s.io/kubernetes/_output/local/go/src/k8s.io/kubernetes/plugin/pkg/scheduler/generic_scheduler.go
         0       20ms (flat, cum) 16.67% of Total . . 230: . . 231:// Checks whether node with a given name and NodeInfo satisfies all predicateFuncs. . . 232:func podFitsOnNode(pod *api.Pod, meta interface{}, info *schedulercache.NodeInfo, predicateFuncs map[string]algorithm.FitPredicate) (bool, []algorithm.PredicateFailureReason, error) { . . 233: var failedPredicates []algorithm.PredicateFailureReason . . 234: for _, predicate := range predicateFuncs { . 20ms 235: fit, reasons, err := predicate(pod, meta, info) . . 236: if err != nil { . . 237: err := fmt.Errorf("SchedulerPredicates failed due to %v, which is unexpected.", err) . . 238: return false, []algorithm.PredicateFailureReason{}, err . . 239: } . . 240: if !fit {
disadm weblist
pprof --help

Memory Profiling

/debug/pprof/profile/debug/pprof/heap
➜  go tool pprof ./hyperkube http://172.16.3.232:10251/debug/pprof/heap        
Fetching profile from http://172.16.3.232:10251/debug/pprof/heap Saved profile in /home/cizixs/pprof/pprof.hyperkube.172.16.3.232:10251.inuse_objects.inuse_space.002.pb.gz Entering interactive mode (type "help" for commands) (pprof)
top N
(pprof) top
11712.11kB of 14785.10kB total (79.22%)
Dropped 580 nodes (cum <= 73.92kB) Showing top 10 nodes out of 146 (cum >= 512.31kB) flat flat% sum% cum cum% 2072.09kB 14.01% 14.01% 2072.09kB 14.01% k8s.io/kubernetes/vendor/github.com/beorn7/perks/quantile.NewTargeted 2049.25kB 13.86% 27.87% 2049.25kB 13.86% k8s.io/kubernetes/pkg/api/v1.(*ResourceRequirements).Unmarshal 1572.28kB 10.63% 38.51% 1572.28kB 10.63% k8s.io/kubernetes/vendor/github.com/beorn7/perks/quantile.(*stream).merge 1571.34kB 10.63% 49.14% 1571.34kB 10.63% regexp.(*bitState).reset 1184.27kB 8.01% 57.15% 1184.27kB 8.01% bytes.makeSlice 1024.16kB 6.93% 64.07% 1024.16kB 6.93% k8s.io/kubernetes/pkg/api/v1.(*ObjectMeta).Unmarshal 613.99kB 4.15% 68.23% 2150.63kB 14.55% k8s.io/kubernetes/pkg/api/v1.(*PersistentVolumeClaimList).Unmarshal 591.75kB 4.00% 72.23% 1103.79kB 7.47% reflect.Value.call 520.67kB 3.52% 75.75% 520.67kB 3.52% k8s.io/kubernetes/vendor/github.com/gogo/protobuf/proto.RegisterType 512.31kB 3.47% 79.22% 512.31kB 3.47% k8s.io/kubernetes/pkg/api/v1.(*PersistentVolumeClaimStatus).Unmarshal

每一列的含义也是类似的,只不过从 CPU 使用时间变成了内存使用大小,就不多解释了。

websvg
--inuse_objects--alloc-space

这里还要提两个比较有用的方法,如果应用比较复杂,生成的调用图特别大,看起来很乱,有两个办法可以优化:

web funcNamego tool pprof--nodefration=0.05

go-torch 和火焰图

火焰图(Flame Graph)是 Bredan Gregg 创建的一种性能分析图表,因为它的样子近似 ?而得名。上面的 profiling 结果也转换成火焰图,如果对火焰图比较了解可以手动来操作,不过这里我们要介绍一个工具:go-torch。这是 uber 开源的一个工具,可以直接读取 golang profiling 数据,并生成一个火焰图的 svg 文件。

火焰图 svg 文件可以通过浏览器打开,它对于调用图的最优点是它是动态的:可以通过点击每个方块来 zoom in 分析它上面的内容。

火焰图的调用顺序从下到上,每个方块代表一个函数,它上面一层表示这个函数会调用哪些函数,方块的大小代表了占用 CPU 使用的长短。火焰图的配色并没有特殊的意义,默认的红、黄配色是为了更像火焰而已。

http://localhost:8080/debug/pprof/profile
-u --url-s --suffix/debug/pprof/profile--seconds
$PATH

和测试工具的集成

go test 命令有两个参数和 pprof 相关,它们分别指定生成的 CPU 和 Memory profiling 保存的文件:

-cpuprofile-memprofile
cpu.prof
$ go test -bench . -cpuprofile=cpu.prof
main.testcpu.profgo tool pprofmain.test

需要注意的是,Profiling 一般和性能测试一起使用,只有应用在负载高的情况下 Profiling 才有意义。通常会高并发测试接口,然后来执行。

现在已经可以不用 go-torch 来生成火焰图了,安装 pprof,如下命令:

go tool pprof 也可以生成cpu graph图,但没有火焰图,而且界面比较挫,pprof则可以生成火焰图

go get -u github.com/google/pprof 

pprof -http 127.0.0.1:9090 http://127.0.0.1:8080/debug/pprof/profile

http://127.0.0.1:8080/debug/pprof/profile,这个是代码里监控pprof的

代码如下:

package main

import (
    "fmt"
    "net/http"

    _ "net/http/pprof"
)

func health(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "[%s] Tequila is up", config.GetDeploymentEnv())
}

func main() {
    router := api.GetRouter()
    router.HandleFunc("/", health).Methods("GET")

    go func() {
        http.ListenAndServe(":8080", nil)
    }()

    base.Main(":80", router)
}

本文内容总结:Profiling,两种收集方式,工具型应用,服务型应用,go tool pprof 命令:获取和分析 Profiling 数据,CPU Profiling,Memory Profiling,go-torch 和火焰图,和测试工具的集成,

原文链接:https://www.cnblogs.com/linguoguo/p/10375224.html