目录

golang性能分析

概述

我们要想分析数据,首先需要拿到prof文件或者叫概要文件。

Go 语言为程序开发者们提供了丰富的性能分析 API,和非常好用的标准工具。 这些 API 主要存在于:

runtime/pprof;

net/http/pprof;

runtime/trace;

这三个代码包中。

另外,runtime代码包中还包含了一些更底层的 API。它们可以被用来收集或输出 Go 程序运行过程中的一些关键指标,并帮助我们生成相应的概要文件以供后续分析时使用。

至于标准工具,主要有go tool pprof和go tool trace这两个。 它们可以解析概要文件中的信息,并以人类易读的方式把这些信息展示出来。

此外,go test命令也可以在程序测试完成后生成概要文件。如此一来,我们就可以很方便地使用前面那两个工具读取概要文件,并对被测程序的性能加以分析。

在 Go 语言中,用于分析程序性能的概要文件有三种,分别是:CPU 概要文件(CPU Profile)、内存概要文件(Mem Profile)和阻塞概要文件(Block Profile)。

这些概要文件中包含的都是:在某一段时间内,对 Go 程序的相关指标进行多次采样后得到的概要信息。对于 CPU 概要文件来说,其中的每一段独立的概要信息都记录着,在进行某一次采样的那个时刻,CPU 上正在执行的 Go 代码。而对于内存概要文件,其中的每一段概要信息都记载着,在某个采样时刻,正在执行的 Go 代码以及堆内存的使用情况,这里包含已分配和已释放的字节数量和对象数量。至于阻塞概要文件,其中的每一段概要信息,都代表着 Go 程序中的一个 goroutine 阻塞事件。

在默认情况下,这些概要文件中的信息并不是普通的文本,它们都是以二进制的形式展现的(protocol buffers)。如果你使用一个常规的文本编辑器查看它们的话,那么肯定会看到一堆“乱码”。这时就可以显现出go tool pprof这个工具的作用了。我们可以通过它进入一个基于命令行的交互式界面,并对指定的概要文件进行查阅。

生成概要文件

通过runtime/pprof包

runtime/pprof:采集程序(非 Server)的运行数据进行分析。 手动调用runtime.StartCPUProfile或者runtime.StopCPUProfile等 API来生成和写入采样文件,灵活性高,通过 runtime/pprof 实现。

func genPprof() {
	cupOut, err := os.OpenFile("cpu.prof", os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		log.Fatal(err)
	}
	defer cupOut.Close()

	// memOut, err := os.OpenFile("mem.prof", os.O_RDWR|os.O_CREATE, 0666)
	// if err != nil {
	// 	log.Fatal(err)
	// }
	// defer memOut.Close()

	pprof.StartCPUProfile(cupOut)
	defer pprof.StopCPUProfile()
	// pprof.WriteHeapProfile(memOut) // 统计内存信息
	for i := 0; i < 1000; i++ {
		getRand()
	}
}

func getRand() {
	rand.Seed(time.Now().UnixNano())
	fmt.Println(rand.Int())
}

net/http/pprof包

net/http/pprof对采集 HTTP Server 的运行时数据进行分析,runtime/pprof和runtime/trace的封装

import (
	_ "net/http/pprof"
)

log.Println(http.ListenAndServe("localhost:8082", nil))

我们就可以通过http://localhost:8082/debug/pprof来查看,这个域名和端口和我们启动服务的时候一致。点击里面的按钮就会开始相应的采样,采样结束就会自动下载概要文件。 ./net_http_pprof_采集.png

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

/debug/pprof/profile:访问这个链接会自动进行 CPU profiling,持续 30s,并生成一个文件供下载 /debug/pprof/heap: Memory Profiling 的路径,访问这个链接会得到一个内存 Profiling 结果的文件 /debug/pprof/block:block Profiling 的路径 /debug/pprof/goroutines:运行的 goroutines 列表,以及调用关系

通过go test获取

初了上面两种方法,go test: 通过 go test -bench . -cpuprofile prof.cpu生成采样文件 适用对函数进行针对性测试。通常使用上面两个进行整体分析,找出热点以后再go test进行分析

func TestTest(t *testing.T) {
	for i := 0; i < 1000; i++ {
		getRand()
	}
}
go test -bench=. -cpuprofile=cpu.prof
go test -bench . -memprofile=./mem.prof

分析概要文件

go tool pprof工具

有两种交互方式,一种是进入命令行,另一种可以指定http,通过web的形式更方便

  1. 命令行方式查看: go tool pprof最简单的使用方式为:

go tool pprof [binary] [source] 其中:

binary 是应用的二进制文件,用来解析各种符号; source 表示 profile 数据的来源,可以是本地的文件,也可以是 http 地址。

go tool pprof cpu.prof

如果是web应用也可以自己指定通过接口去拿,下载和分析合为一步:

go tool pprof http://localhost:8082/debug/pprof/profile

它要去下载,然后进入命令行,在命令行里通过命令查看 常用的命令有top,可以输入help查看支持的所有命令。

我们可以在交互界面输入top3来查看程序中占用CPU前3位的函数:

(pprof) top3
Showing nodes accounting for 100.37s, 87.68% of 114.47s total
Dropped 17 nodes (cum <= 0.57s)
Showing top 3 nodes out of 4
      flat  flat%   sum%        cum   cum%
    42.52s 37.15% 37.15%     91.73s 80.13%  runtime.selectnbrecv
    35.21s 30.76% 67.90%     39.49s 34.50%  runtime.chanrecv
    22.64s 19.78% 87.68%    114.37s 99.91%  main.logicCode

其中:

flat:当前函数占用CPU的耗时 flat::当前函数占用CPU的耗时百分比 sun%:函数占用CPU的耗时累计百分比 cum:当前函数加上调用当前函数的函数占用CPU的总耗时 cum%:当前函数加上调用当前函数的函数占用CPU的总耗时百分比 最后一列:函数名称 在大多数的情况下,我们可以通过分析这五列得出一个应用程序的运行情况,并对程序进行优化。

  1. 通过web形式查看

命令行查看不方便?只需要在上面的命令中加个参数即可 -http

go tool pprof -http localhost:8088 cpu.prof

这个localhost:8088是自己随意指定的。 键入命令后自动打开浏览器http://localhost:8088/ui/

r如果没有安装graphviz,Could not execute dot; may need to install graphviz.Mac安装:brew install graphviz

./web查看pprof.png

trace大杀器

单单使用 PProf 有时候不一定足够完整,因为在真实的程序中还包含许多的隐藏动作,例如 Goroutine 在执行时会做哪些操作?执行/阻塞了多长时间?在什么时候阻止?在哪里被阻止的?谁又锁/解锁了它们?GC 是怎么影响到 Goroutine 的执行的?这些东西用 PProf 是很难分析出来的。 其实web应用中我们import _ “net/http/pprof"时通过web就能点击view->trace拿到trace文件。 不过官方还提供了如下方法

func main() {
	traceOut, err := os.OpenFile("trace.out", os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		log.Fatal(err)
	}
	defer traceOut.Close()

	 trace.Start(traceOut)
	defer trace.Stop()
	go func() {
		fmt.Println("new go")
	}()
	time.Sleep(time.Second)
}

有了trace文件,就可以使用go tool trace trace.out自动打开浏览器。 ./trace_web.png

View trace:查看跟踪 Goroutine analysis:Goroutine 分析 Network blocking profile:网络阻塞概况 Synchronization blocking profile:同步阻塞概况 Syscall blocking profile:系统调用阻塞概况 Scheduler latency profile:调度延迟概况 User defined tasks:用户自定义任务 User defined regions:用户自定义区域 Minimum mutator utilization:最低 Mutator 利用率

gin包性能分析

使用gin包开启web,如果再使用import _ “net/http/pprof"是没有用的。可以使用第三方包。

go get https://github.com/gin-contrib/pprof

示例代码:

package main

import (
    "net/http"

    "github.com/gin-contrib/pprof"
    "github.com/gin-gonic/gin"
)

func main() {
    app := gin.Default()

    pprof.Register(app) // 性能

    app.GET("/test", func(c *gin.Context) {
        c.String(http.StatusOK, "test")
    })
    app.Run(":3000")
}

代码运行之后可以看到系统自动增加了很多/debug/pprof的API。通过这些API我们可以看到需要的数据。使用起来和官方的web没有什么差别。