26.4 用 pprof 调试

要监控Go程序的堆栈,CPU的耗时等性能信息,我们可以通过使用pprof包来实现。在代码中,pprof包有两种方式导入:

"net/http/pprof"
"runtime/prof"

其实net/http/pprof中只是使用runtime/pprof包来进行封装了一下,并在http端口上暴露出来,让我们可以在浏览器查看程序的性能分析。我们可以自行查看net/http/pprof中代码,只有一个文件pprof.go。

下面我们具体说说怎么使用pprof,首先我们讲讲在开发中取得pprof信息的三种方式:

一:Web 服务器程序

如果我们的Go程序是Web服务器,你想查看自己的Web服务器的状态。这个时候就可以选择net/http/pprof。你只需要引入包_"net/http/pprof",然后就可以在浏览器中使用http://localhost:port/debug/pprof/直接看到当前Web服务的状态,包括CPU占用情况和内存使用情况等。

这里port是8080,也就是我们Web服务器监听的端口。

package main
import (
	"fmt"
	"net/http"
	_ "net/http/pprof"  // 为什么用_ , 在讲解http包时有解释。
)
func myfunc(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "hi")
}
func main() {
	http.HandleFunc("/", myfunc)
	http.ListenAndServe(":8080", nil)
}

访问http://localhost:8080/debug/pprof/

二:服务进程

如果你的Go程序不是Web服务器,而是一个服务进程,可以选择使用net/http/pprof包,然后开启一个goroutine来监听相应端口。

package main
import (
	"fmt"
	"log"
	"net/http"
	_ "net/http/pprof"
	"time"
)
func main() {
	// 开启pprof
	go func() {
		log.Println(http.ListenAndServe("localhost:8080", nil))
	}()
	go hello()
	select {}
}
func hello() {
	for {
		go func() {
			fmt.Println("hello word")
		}()
		time.Sleep(time.Millisecond * 1)
	}
}

访问http://localhost:8080/debug/pprof/ 在前面这两种方式中,我们还可以在命令行分别运行以下命令:

  • 利用这个命令查看堆栈信息:go tool pprof http://localhost:8080/debug/pprof/heap
  • 利用这个命令可以查看程序CPU使用情况信息:go tool pprof http://localhost:8080/debug/pprof/profile
  • 使用这个命令可以查看block信息:go tool pprof http://localhost:8080/debug/pprof/block

这里需要先安装graphviz,http://www.graphviz.org/download/ ,windows平台直接下载zip包,解压缩后把bin目录放到 $path 中。我们可以通过执行命令 png 产生图片,还有svg,gif,pdf等命令,生成的图片自动命名存放在当前目录下,我们这里生成了png。其他命令使用可通过help查看。

三:应用程序

如果你的Go程序只是一个应用程序,那么你就不能使用net/http/pprof包了,你就需要使用到runtime/pprof。比如下面的例子:

package main
import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/pprof"
	"time"
)
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
func Factorial(n uint64) (result uint64) {
	if n > 0 {
		result = n * Factorial(n-1)
		return result
	}
	return 1
}
func main() {
	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	go compute()
	time.Sleep(10 * time.Second)
}
func compute() {
	for i := 0; i < 100; i++ {
		go func() {
			fmt.Println(Factorial(uint64(40)))
		}()
		time.Sleep(time.Millisecond * 1)
	}
}

编译后生成3.exe文件并运行:

3.exe --cpuprofile=cpu.prof

这里我们编译后可执行程序是3.exe , 程序运行完后的CPU信息就会记录到cpu.prof中。现在有了cpu.prof 文件,我们就可以通过go tool pprof 来看相应的信息了。在命令行运行:

go tool pprof 3.exe cpu.prof 

这里要注意的是需要带上可执行的程序名以及prof信息文件。命令执行后会进入到:

命令界面和前面两种使用net/http/pprof包 一样。我们可以通过go tool pprof 生svg,png或者是pdf文件。

这是生成的png文件,和前面生成的png类似,前面我们生成的是block信息:

通过上面这三种情况的分析,我们可以知道,其实就是两种情况: go tool pprof http://localhost:8080/debug/pprof/profile 这种url方式,或者 go tool pprof 3.exe cpu.prof 这种文件方式来进行分析。

我们可以根据项目情况灵活使用。有关pprof,我们就讲这么多,在实际项目中,我们多使用就会发现这个工具还是蛮有用处的。