菜单
开源

Go(推送模式)

我们的 Go Profiler 是一款旨在优化 Golang 应用程序的前沿工具。通过与 Pyroscope 集成,该 profiler 为开发人员提供了对其 Go 代码库的深入视图,从而实现实时的性能分析。这款强大的工具对于查明低效率、简化代码执行以及确保 Go 应用程序的最佳性能至关重要。

Pyroscope 使用标准的 runtime/pprof 包来收集 profiling 数据。有关详细信息,请参阅官方文档

注意

有关 Go 支持的 profile 类型列表,请参阅可用的 profiling 类型

开始之前

要捕获和分析 profiling 数据,您需要一个托管的 Pyroscope OSS 服务器或一个托管的 带有 Grafana Cloud Profiles 的 Pyroscope 实例(需要一个免费的 Grafana Cloud 帐户)。

Pyroscope 服务器可以是用于开发的本地服务器,也可以是用于生产环境的远程服务器。

配置 Go 客户端

要开始 profiling Go 应用程序,您需要在您的应用程序中包含 Go 模块

Go
go get github.com/grafana/pyroscope-go

注意

如果您更喜欢使用拉取模式,您可以使用 Grafana Alloy 来实现。

将以下代码添加到您的应用程序中

Go
package main

import "github.com/grafana/pyroscope-go"

func main() {
  // These 2 lines are only required if you're using mutex or block profiling
  // Read the explanation below for how to set these rates:
  runtime.SetMutexProfileFraction(5)
  runtime.SetBlockProfileRate(5)

  pyroscope.Start(pyroscope.Config{
    ApplicationName: "simple.golang.app",

    // replace this with the address of pyroscope server
    ServerAddress:   "http://pyroscope-server:4040",

    // you can disable logging by setting this to nil
    Logger:          pyroscope.StandardLogger,

    // you can provide static tags via a map:
    Tags:            map[string]string{"hostname": os.Getenv("HOSTNAME")},

    ProfileTypes: []pyroscope.ProfileType{
      // these profile types are enabled by default:
      pyroscope.ProfileCPU,
      pyroscope.ProfileAllocObjects,
      pyroscope.ProfileAllocSpace,
      pyroscope.ProfileInuseObjects,
      pyroscope.ProfileInuseSpace,

      // these profile types are optional:
      pyroscope.ProfileGoroutines,
      pyroscope.ProfileMutexCount,
      pyroscope.ProfileMutexDuration,
      pyroscope.ProfileBlockCount,
      pyroscope.ProfileBlockDuration,
    },
  })

  // your code goes here
}

或者,如果您想更好地控制 profiling 过程,您可以手动处理 profiler 的初始化和终止

Go
  profiler, err := pyroscope.Start(pyroscope.Config{
    // omitted for brevity 
  })
  if err != nil {
    // the only reason this would fail is if the configuration is not valid
    log.Fatalf("failed to start Pyroscope: %v", err)
  }
  defer profiler.Stop()

  // your code goes here
}

如果您需要确保在应用程序退出之前发送最后一个 profile,则此方法可能是必要的。

将 profiling 标签添加到您的应用程序中

您可以将标签添加到 profiling 数据中。这些标签可用于在 UI 中过滤数据。有一个自定义 API,它与 Go 原生的 pprof API 一致

Go
// these two ways of adding tags are equivalent:
pyroscope.TagWrapper(context.Background(), pyroscope.Labels("controller", "slow_controller"), func(c context.Context) {
  slowCode()
})

pprof.Do(context.Background(), pprof.Labels("controller", "slow_controller"), func(c context.Context) {
  slowCode()
})

Mutex profiling

Mutex profiling 对于查找应用程序内部的争用源非常有用。它可以帮助您找出哪些互斥锁正被哪些 goroutine 持有。

要启用互斥锁 profiling,您需要在您的应用程序中添加以下代码

Go
runtime.SetMutexProfileFraction(rate)

rate 参数控制互斥锁争用事件在互斥锁 profile 中报告的比例。平均而言,会报告 1/rate 个事件。

Block profiling

Block profiling 让您可以分析您的程序在阻塞操作上花费了多少时间,例如

  • select
  • channel 发送/接收
  • semacquire
  • notifyListWait

要启用 block profiling,您需要在您的应用程序中添加以下代码

Go
runtime.SetBlockProfileRate(rate)

rate 参数控制 goroutine 阻塞事件在阻塞 profile 中报告的比例。profiler 旨在对每个 rate 纳秒的阻塞时间平均采样一个阻塞事件。

将数据发送到 Pyroscope OSS 或 Grafana Cloud Profiles

要配置 Golang SDK 将数据发送到 Pyroscope,请将 <URL> 占位符替换为相应的服务器 URL。这可以是 Grafana Cloud URL 或您自己的自定义 Pyroscope 服务器 URL。

如果您需要将数据发送到 Grafana Cloud,您需要配置 HTTP 基本身份验证。将 <User> 替换为您的 Grafana Cloud 堆栈用户,并将 <Password> 替换为您的 Grafana Cloud API 密钥。

如果您的 Pyroscope 服务器启用了多租户,您需要配置租户 ID。将 <TenantID> 替换为您的 Pyroscope 租户 ID。

Go
pyroscope.Start(pyroscope.Config{
  ApplicationName:   "example.golang.app",
  ServerAddress:     "<URL>",
  // Optional HTTP Basic authentication
  BasicAuthUser:     "<User>",
  BasicAuthPassword: "<Password>",
  // Optional Pyroscope tenant ID (only needed if using multi-tenancy). Not needed for Grafana Cloud.
  // TenantID:          "<TenantID>",
  ProfileTypes: []pyroscope.ProfileType{
    pyroscope.ProfileCPU,
    pyroscope.ProfileInuseObjects,
    pyroscope.ProfileAllocObjects,
    pyroscope.ProfileInuseSpace,
    pyroscope.ProfileAllocSpace,
  },
})

选项:使用 DisableGCRuns 处理增加的内存使用量

当跟踪大量对象时,Pyroscope 可能需要额外的资源。例如,索引大量数据的 Go 服务需要更多内存。这种跟踪可能会导致更高的 CPU 使用率和潜在的 CPU 限制。

您可以在 Go 配置中使用 DisableGCRuns 来禁用自动运行时。如果此标志被激活,则 GC 运行次数较少,因此 CPU 资源消耗也较少。但是,堆 profile 可能不太精确。

背景

在 Go 的 pprof 堆 profiling 中,强制垃圾回收 (GC) 通过删除未收集的对象来确保准确的内存使用快照。如果没有此步骤,堆 profile 可能包含已分配但不再使用的内存——这些对象之所以留在内存中,仅仅是因为它们尚未被收集。这可能会掩盖或模仿内存泄漏,并给 profile 引入偏差,从而使分析变得复杂。因此,Pyroscope 默认在每次收集堆 profile 时强制执行 GC。

但是,在某些情况下,强制 GC 可能会增加 CPU 使用率,尤其是在堆中有许多活动对象的情况下。此问题反映在 CPU profile 中 runtime.GC 函数的出现。如果问题已显现,并且堆 profile 中的一些不准确性是可以接受的,那么建议禁用此选项以避免性能下降。

激活 DisableGCRuns

DisableGCRuns: true 添加到 pyroscope.Start(pyroscope.Config) 代码块中。

Go
pyroscope.Start(pyroscope.Config{
  ApplicationName:   "example.golang.app",
  ServerAddress:     "<URL>",
  // Disable automatic runtime.GC runs between getting the heap profiles.
		DisableGCRuns:   true,

Golang profiling 示例

查看以下资源以了解有关 Golang profiling 的更多信息