go程序中使用pprof增加程序监控功能

1.什么是 pprof?
pprof 是 Go 内置的性能分析工具,用于生成程序运行时的性能分析数据。它可以帮助开发者分析:

CPU 使用情况
内存分配情况
Goroutine 状况
系统调用分析等
2. 如何使用 pprof?
要使用 pprof,首先需要在 Go 代码中引入 net/http/pprof 包,并启动一个 HTTP 服务器以提供性能分析接口。

  1. 使用 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))
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值