3.1. 性能分析 (Profiling)

3.1.1. pprof 概述

Go 内置了强大的性能分析工具 pprof,支持以下类型的分析:

类型

描述

CPU

CPU 使用情况分析

Heap

堆内存分配分析

Goroutine

Goroutine 栈分析

Block

阻塞操作分析

Mutex

互斥锁竞争分析

Trace

执行追踪

3.1.2. 开启 pprof

3.1.2.1. 方式 1:HTTP 服务(推荐)

import (
    "net/http"
    _ "net/http/pprof" // 匿名导入,自动注册 handler
)

func main() {
    // 在单独的 goroutine 中启动 pprof 服务
    go func() {
        http.ListenAndServe(":6060", nil)
    }()
    
    // 主程序逻辑
    runMainApplication()
}

访问端点:

  • http://localhost:6060/debug/pprof/ - 总览页面

  • http://localhost:6060/debug/pprof/heap - 堆内存

  • http://localhost:6060/debug/pprof/goroutine - Goroutine

  • http://localhost:6060/debug/pprof/profile - CPU (需要参数 seconds)

3.1.2.2. 方式 2:代码中手动收集

import (
    "os"
    "runtime/pprof"
)

func main() {
    // CPU 分析
    cpuFile, _ := os.Create("cpu.prof")
    defer cpuFile.Close()
    pprof.StartCPUProfile(cpuFile)
    defer pprof.StopCPUProfile()
    
    // 你的程序逻辑
    doWork()
    
    // 内存分析
    memFile, _ := os.Create("mem.prof")
    defer memFile.Close()
    pprof.WriteHeapProfile(memFile)
}

3.1.3. CPU 性能分析

3.1.3.1. 收集 CPU Profile

# 方式 1:通过 HTTP
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

# 方式 2:从文件
go tool pprof cpu.prof

3.1.3.2. pprof 交互式命令

(pprof) top          # 显示消耗最多 CPU 的函数
(pprof) top10        # 显示前 10 个
(pprof) list funcName # 显示函数的源码及耗时
(pprof) web          # 在浏览器中打开调用图
(pprof) svg          # 生成 SVG 图
(pprof) pdf          # 生成 PDF 报告

3.1.3.3. 示例输出

(pprof) top10
Showing nodes accounting for 2.10s, 84.00% of 2.50s total
Showing top 10 nodes out of 50
      flat  flat%   sum%        cum   cum%
     0.70s 28.00% 28.00%      0.70s 28.00%  runtime.memmove
     0.30s 12.00% 40.00%      0.50s 20.00%  main.processData
     0.25s 10.00% 50.00%      0.25s 10.00%  runtime.mallocgc
     ...

3.1.3.4. 理解指标

  • flat: 函数自身消耗的时间

  • cum: 函数及其调用的所有函数消耗的时间

  • flat%: flat 时间占总时间的百分比

  • cum%: cum 时间占总时间的百分比

3.1.4. 内存分析

3.1.4.1. 收集 Heap Profile

go tool pprof http://localhost:6060/debug/pprof/heap

3.1.4.2. 分析内存分配

# 查看当前堆内存使用
(pprof) top

# 查看累计分配(包括已释放的)
(pprof) top -cum

# 查看分配次数而非字节数
go tool pprof -alloc_objects http://localhost:6060/debug/pprof/heap

3.1.4.3. 内存分析类型

参数

描述

-inuse_space

当前使用的内存 (默认)

-inuse_objects

当前使用的对象数

-alloc_space

累计分配的内存

-alloc_objects

累计分配的对象数

3.1.5. Goroutine 分析

3.1.5.1. 查看 Goroutine 状态

go tool pprof http://localhost:6060/debug/pprof/goroutine

3.1.5.2. 检测 Goroutine 泄漏

func checkGoroutineLeak() {
    before := runtime.NumGoroutine()
    
    // 执行可能泄漏的操作
    doSomething()
    
    // 等待 goroutine 完成
    time.Sleep(time.Second)
    
    after := runtime.NumGoroutine()
    if after > before {
        fmt.Printf("可能存在 Goroutine 泄漏: %d -> %d\n", before, after)
    }
}

3.1.6. 阻塞分析

需要先开启阻塞分析:

import "runtime"

func init() {
    runtime.SetBlockProfileRate(1) // 开启阻塞分析
}
go tool pprof http://localhost:6060/debug/pprof/block

3.1.7. Mutex 竞争分析

需要先开启 Mutex 分析:

import "runtime"

func init() {
    runtime.SetMutexProfileFraction(1) // 开启 mutex 分析
}
go tool pprof http://localhost:6060/debug/pprof/mutex

3.1.8. Trace 分析

Trace 提供更细粒度的执行追踪。

3.1.8.1. 收集 Trace

import (
    "os"
    "runtime/trace"
)

func main() {
    f, _ := os.Create("trace.out")
    defer f.Close()
    
    trace.Start(f)
    defer trace.Stop()
    
    // 你的程序逻辑
}

3.1.8.2. 查看 Trace

go tool trace trace.out

Trace 可以显示:

  • Goroutine 调度

  • 网络 I/O

  • 系统调用

  • GC 活动

  • 用户自定义事件

3.1.9. 可视化工具

3.1.9.1. 使用 go tool pprof Web UI

# 需要安装 graphviz
brew install graphviz

# 启动 Web UI
go tool pprof -http=:8080 cpu.prof

3.1.9.2. Flame Graph (火焰图)

# 安装 go-torch(已集成到 go tool pprof)
go tool pprof -http=:8080 -flame cpu.prof

3.1.10. 生产环境最佳实践

3.1.10.1. 1. 安全暴露 pprof

import (
    "net/http"
    "net/http/pprof"
)

func main() {
    // 只在内部网络监听
    mux := http.NewServeMux()
    mux.HandleFunc("/debug/pprof/", pprof.Index)
    mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
    mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
    mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
    mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
    
    // 只监听内网地址
    go http.ListenAndServe("127.0.0.1:6060", mux)
}

3.1.10.2. 2. 持续性能监控

import (
    "os"
    "runtime/pprof"
    "time"
)

func periodicMemProfile() {
    ticker := time.NewTicker(5 * time.Minute)
    for range ticker.C {
        f, err := os.Create(fmt.Sprintf("mem-%d.prof", time.Now().Unix()))
        if err != nil {
            continue
        }
        pprof.WriteHeapProfile(f)
        f.Close()
    }
}

3.1.10.3. 3. 自定义 pprof Label

import "runtime/pprof"

func handleRequest(ctx context.Context, userID string) {
    labels := pprof.Labels("user_id", userID)
    pprof.Do(ctx, labels, func(ctx context.Context) {
        // 处理请求
        processRequest(ctx)
    })
}

3.1.11. 常用分析流程

        flowchart TD
    A[发现性能问题] --> B{确定问题类型}
    B -->|CPU 高| C[CPU Profile]
    B -->|内存高| D[Heap Profile]
    B -->|响应慢| E[Trace/Block Profile]
    B -->|死锁| F[Goroutine/Mutex Profile]
    
    C --> G[找出热点函数]
    D --> H[找出内存分配点]
    E --> I[找出阻塞点]
    F --> J[找出锁竞争]
    
    G --> K[优化代码]
    H --> K
    I --> K
    J --> K
    
    K --> L[验证效果]
    L -->|未解决| B
    L -->|已解决| M[完成]
    

3.1.12. 参考资源