1.什么是 pprof?
pprof 是 Go 内置的性能分析工具,用于生成程序运行时的性能分析数据。它可以帮助开发者分析:
CPU 使用情况
内存分配情况
Goroutine 状况
系统调用分析等
2. 如何使用 pprof?
要使用 pprof,首先需要在 Go 代码中引入 net/http/pprof 包,并启动一个 HTTP 服务器以提供性能分析接口。
- 使用 pprof 分析 Go 程序
启动 Go 程序:
go run main.go
启动程序后,访问 http://localhost:6060/debug/pprof/,查看提供的接口。
假设你要分析 30 秒的 CPU 性能:
go tool pprof http://localhost:6060/debug/pprof/cpu?seconds=30
运行后,下载到本地的 CPU 分析数据文件为 cpu.pprof,然后可以使用 go tool pprof 进一步分析。
例如,使用以下命令查看分析结果:
go tool pprof cpu.pprof
在 pprof 的交互界面中,你可以输入 top 查看 CPU 消耗最多的函数,或者使用 web 生成 SVG 图形查看分析结果。
示例代码如下:
package util
import (
log "github.com/sirupsen/logrus"
"net/http"
"runtime"
"strconv"
"sync"
"github.com/gorilla/mux"
"net/http/pprof"
)
// 定义全局 pprof 控制开关和互斥锁
var (
enablePprof bool
enablePprofMu sync.Mutex
blockRate int // 新增阻塞采样率
mutexFraction int // 新增互斥锁采样比例
configMu sync.Mutex // 新增配置锁
)
func InitPprof() {
// 初始化 pprof 状态
enablePprof = false
// 创建路由器
r := mux.NewRouter()
// 注册 pprof 相关路由
//curl http://localhost:6060/debug/pprof/enbale
r.HandleFunc("/debug/pprof/enable", pprofEnableHandler)
//curl http://localhost:6060/debug/pprof/disable
r.HandleFunc("/debug/pprof/disable", pprofDisableHandler)
// 新增配置接口
//curl http://localhost:6060/debug/pprof/set-block-rate?rate=1000
r.HandleFunc("/debug/pprof/set-block-rate", setBlockRateHandler)
//curl http://localhost:6060/debug/pprof/set-mutex-fraction?fraction=1
r.HandleFunc("/debug/pprof/set-mutex-fraction", setMutexFractionHandler)
// 启动 pprof 服务
go startPprofServer(r)
}
// 新增阻塞采样率设置处理器
// rate值 实际采样间隔 适用场景
// 0 完全禁用 生产环境默认配置
// 1 记录所有阻塞事件 精确调试但性能开销最大
// 1000 约1微秒采样一次 平衡精度与开销的通用配置
// 1e6 约1毫秒采样一次 高并发场景下的低开销采样配置
func setBlockRateHandler(w http.ResponseWriter, r *http.Request) {
rate, err := strconv.Atoi(r.URL.Query().Get("rate"))
if err != nil || rate < 0 {
http.Error(w, "Invalid rate parameter", http.StatusBadRequest)
return
}
configMu.Lock()
defer configMu.Unlock()
blockRate = rate
runtime.SetBlockProfileRate(blockRate)
if _, err := w.Write([]byte("Block profile rate updated")); err != nil {
log.Errorf("Failed to write enable response: %v", err)
}
}
// 新增互斥锁采样设置处理器
//行为逻辑:
//当参数fraction ≤ 0 时:关闭互斥锁采样
//当参数fraction = 1 时:记录所有互斥锁争用事件
//当参数fraction > 1 时:按比例采样(例如设为4时,每4次争用事件采样1次)
func setMutexFractionHandler(w http.ResponseWriter, r *http.Request) {
fraction, err := strconv.Atoi(r.URL.Query().Get("fraction"))
if err != nil || fraction < 0 {
http.Error(w, "Fraction must be 0 or 1", http.StatusBadRequest)
return
}
configMu.Lock()
defer configMu.Unlock()
mutexFraction = fraction
runtime.SetMutexProfileFraction(mutexFraction)
if _, err := w.Write([]byte("Mutex profile fraction updated")); err != nil {
log.Errorf("Failed to write enable response: %v", err)
}
}
// 启动 pprof 服务
func startPprofServer(r *mux.Router) {
pprofRouter := r.PathPrefix("/debug/pprof/").Subrouter()
// 通用包装函数
wrapPprofHandler := func(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
checkAndHandlePprof(w, r, handler)
}
}
// 注册标准路径(经过包装)
pprofRouter.HandleFunc("/", wrapPprofHandler(pprof.Index))
pprofRouter.HandleFunc("/cmdline", wrapPprofHandler(pprof.Cmdline))
pprofRouter.HandleFunc("/profile", wrapPprofHandler(pprof.Profile))
pprofRouter.HandleFunc("/symbol", wrapPprofHandler(pprof.Symbol))
pprofRouter.HandleFunc("/trace", wrapPprofHandler(pprof.Trace))
// 注册特殊路径(经过包装)
profiles := []string{"allocs", "heap", "goroutine", "block", "mutex", "threadcreate"}
for _, profile := range profiles {
pprofRouter.Handle(
"/"+profile,
wrapPprofHandler(pprof.Handler(profile).ServeHTTP), // 关键修改
)
}
// 启动 pprof 监听
if err := http.ListenAndServe(":6060", r); err != nil {
log.Errorf("pprof server failed: %v", err)
}
}
func pprofEnableHandler(w http.ResponseWriter, r *http.Request) {
enablePprofMu.Lock()
defer enablePprofMu.Unlock()
enablePprof = true
if _, err := w.Write([]byte("pprof enabled")); err != nil {
log.Errorf("Failed to write enable response: %v", err)
}
}
func pprofDisableHandler(w http.ResponseWriter, r *http.Request) {
enablePprofMu.Lock()
defer enablePprofMu.Unlock()
enablePprof = false
// 关闭互斥锁以及阻塞采样
runtime.SetBlockProfileRate(0)
runtime.SetMutexProfileFraction(0)
if _, err := w.Write([]byte("pprof disabled")); err != nil {
log.Errorf("Failed to write disable response: %v", err)
}
}
// 检查 pprof 启用状态并处理请求
func checkAndHandlePprof(w http.ResponseWriter, r *http.Request, handler func(http.ResponseWriter, *http.Request)) {
enablePprofMu.Lock()
defer enablePprofMu.Unlock()
if !enablePprof {
http.Error(w, "pprof is disabled", http.StatusForbidden)
return
}
handler(w, r)
}
之后在main函数中启动:
util2.InitPprof()
其中github.com/gorilla/mux 是 Go 语言中的一个非常流行的路由(Router)包,它提供了一个强大的 HTTP 路由器,能够帮助开发者更方便地定义 HTTP 路由规则,进行 URL 路由匹配、请求处理等操作
例如:
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
// 处理根路由
func HomeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Welcome to the Home Page!")
}
// 处理带有路径变量的路由
func UserHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
userId := vars["id"]
fmt.Fprintf(w, "User ID: %s\n", userId)
}
// 处理查询参数的路由
func ArticleHandler(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("query")
fmt.Fprintf(w, "Article Search Query: %s\n", query)
}
func main() {
// 创建一个新的路由器
r := mux.NewRouter()
// 定义路由规则
r.HandleFunc("/", HomeHandler)
r.HandleFunc("/user/{id:[0-9]+}", UserHandler) // 路径中带有动态变量 id
r.HandleFunc("/article", ArticleHandler) // 路由匹配查询参数
// 启动 HTTP 服务器
log.Println("Starting server on :8080")
log.Fatal(http.ListenAndServe(":8080", r))
}