Go Wiki: CustomPprofProfiles

最初发布于 https://rakyll.org/custom-profiles/


Go 开箱即用地提供了几个 pprof profile,用于从 Go 程序收集分析数据。

runtime/pprof 包提供的内置 profile

  • profile:CPU profile 确定程序在积极消耗 CPU 周期时(而非睡眠或等待 I/O 时)将时间花在了哪里。
  • heap:Heap profile 报告当前活动的分配;用于监控当前内存使用情况或检查内存泄漏。
  • threadcreate:Thread creation profile 报告导致创建新 OS 线程的程序部分。
  • goroutine:Goroutine profile 报告所有当前 goroutine 的堆栈跟踪。
  • block:Block profile 显示 goroutine 在等待同步原语(包括定时器通道)时被阻塞的位置。Block profile 默认未启用;使用 runtime.SetBlockProfileRate 来启用它。
  • mutex:Mutex profile 报告锁的争用。当您认为 CPU 未被充分利用是由于互斥锁争用时,请使用此 profile。Mutex profile 默认未启用,请参阅 runtime.SetMutexProfileFraction 来启用。

除了内置 profile,runtime/pprof 包还允许您导出自定义 profile,并对代码进行仪器化以记录对该 profile 有贡献的执行堆栈。

想象一下我们有一个 blob 服务器,我们正在为其编写一个 Go 客户端。我们的用户希望能够对客户端上的已打开 blob 进行分析。我们可以创建一个 profile 并记录打开和关闭 blob 的事件,这样用户就可以随时知道有多少 blob 已打开。

这是一个允许您打开一些 blob 的 blobstore 包。我们将创建一个新的自定义 profile 并开始记录导致 blob 打开的执行堆栈。

package blobstore

import "runtime/pprof"

var openBlobProfile = pprof.NewProfile("blobstore.Open")

// Open opens a blob, all opened blobs need
// to be closed when no longer in use.
func Open(name string) (*Blob, error) {
    blob := &Blob{name: name}
    // TODO: Initialize the blob...

    openBlobProfile.Add(blob, 2) // add the current execution stack to the profile
    return blob, nil
}

当用户想要关闭 blob 时,我们需要从 profile 中移除与当前 blob 关联的执行堆栈。

// Close closes the blob and frees the
// underlying resources.
func (b *Blob) Close() error {
    // TODO: Free other resources.
    openBlobProfile.Remove(b)
    return nil
}

现在,从使用此包的程序中,我们应该能够检索 blobstore.Open profile 数据,并使用我们日常的 pprof 工具来检查和可视化它们。

让我们编写一个打开一些 blob 的小型 main 程序。

package main

import (
    "fmt"
    "math/rand"
    "net/http"
    _ "net/http/pprof" // as a side effect, registers the pprof endpoints.
    "time"

    "myproject.org/blobstore"
)

func main() {
    for i := 0; i < 1000; i++ {
        name := fmt.Sprintf("task-blob-%d", i)
        go func() {
            b, err := blobstore.Open(name)
            if err != nil {
                // TODO: Handle error.
            }
            defer b.Close()

            // TODO: Perform some work, write to the blob.
        }()
    }
    http.ListenAndServe("localhost:8888", nil)
}

启动服务器,然后使用 go tool 读取和可视化 profile 数据。

$ go tool pprof https://:8888/debug/pprof/blobstore.Open
(pprof) top
Showing nodes accounting for 800, 100% of 800 total
      flat  flat%   sum%        cum   cum%
       800   100%   100%        800   100%  main.main.func1 /Users/jbd/src/hello/main.go

您会看到有 800 个打开的 blob,并且所有打开操作都来自 main.main.func1。在这个小例子中,没有更多可看的了,但在复杂的服务器中,您可以检查与打开的 blob 交互最热点的地方,并找出瓶颈或泄漏。


此内容是 Go Wiki 的一部分。