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 boolenablePprofMu sync.MutexblockRate int // 新增阻塞采樣率mutexFraction int // 新增互斥鎖采樣比例configMu sync.Mutex // 新增配置鎖
)func InitPprof() {// 初始化 pprof 狀態enablePprof = false// 創建路由器r := mux.NewRouter()// 注冊 pprof 相關路由//curl http://localhost:6060/debug/pprof/enbaler.HandleFunc("/debug/pprof/enable", pprofEnableHandler)//curl http://localhost:6060/debug/pprof/disabler.HandleFunc("/debug/pprof/disable", pprofDisableHandler)// 新增配置接口//curl http://localhost:6060/debug/pprof/set-block-rate?rate=1000r.HandleFunc("/debug/pprof/set-block-rate", setBlockRateHandler)//curl http://localhost:6060/debug/pprof/set-mutex-fraction?fraction=1r.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 = rateruntime.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 = fractionruntime.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 = trueif _, 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 mainimport ("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) // 路徑中帶有動態變量 idr.HandleFunc("/article", ArticleHandler) // 路由匹配查詢參數// 啟動 HTTP 服務器log.Println("Starting server on :8080")log.Fatal(http.ListenAndServe(":8080", r))
}