首页 资源列表 文章列表

go微服务封装的开箱即用的监测模块

第一步:创建公共模块 (monitor/monitor.go)

在你的项目中创建一个 monitor 目录,并新建 monitor.go 文件:

package monitor
import (
	"net/http"
	"time"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
	// 1. 定义请求总数指标
	httpRequestsTotal = promauto.NewCounterVec(
		prometheus.CounterOpts{
			Name: "http_requests_total",
			Help: "Total number of HTTP requests",
		},
		[]string{"method", "path", "status"},
	)
	// 2. 定义请求耗时指标
	httpRequestDuration = promauto.NewHistogramVec(
		prometheus.HistogramOpts{
			Name:    "http_request_duration_seconds",
			Help:    "HTTP request latency distribution",
			Buckets: prometheus.DefBuckets,
		},
		[]string{"method", "path"},
	)
)


// Init 初始化监控模块,传入 Gin 引擎即可一键接入
func Init(r *gin.Engine) {
	// 注册 Go 运行时指标(Goroutine、GC、内存等)
	prometheus.MustRegister(prometheus.NewGoCollector())


	// 暴露 /metrics 接口(供 Prometheus 拉取数据)
	r.GET("/metrics", gin.WrapH(promhttp.Handler()))


	// 暴露 /healthz 健康检查接口(供 K8s 或负载均衡器探活)
	r.GET("/healthz", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"status": "UP"})
	})


	// 注册全局监控中间件
	r.Use(monitorMiddleware())
}


// 内部监控中间件
func monitorMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		
		// 执行后续的业务 Handler
		c.Next()
		
		// 请求结束后,记录指标
		duration := time.Since(start).Seconds()
		status := c.Writer.Status()
		
		// 使用路由模板(如 /user/:id),防止标签基数爆炸
		path := c.FullPath()
		if path == "" {
			path = "unknown" // 处理未匹配到路由的 404 请求
		}


		httpRequestsTotal.WithLabelValues(c.Request.Method, path, string(rune(status))).Inc()
		httpRequestDuration.WithLabelValues(c.Request.Method, path).Observe(duration)
	}
}

第二步:在你的微服务中使用 (main.go)

封装好之后,你的微服务主程序将变得极其简洁:

package main
import (
	"your_project/monitor" // 引入你刚才创建的公共模块
	"github.com/gin-gonic/gin"
)
func main() {
	r := gin.Default()
	// 🎉 核心:只需这一行代码,即可自动注入 /metrics、/healthz 以及全局监控中间件
	monitor.Init(r) 
	// 下面只需要写你的纯业务代码
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "pong"})
	})
	r.Run(":8080")
}

避坑说明

防标签基数爆炸:在中间件中,我使用了 c.FullPath() 而不是 c.Request.URL.Path。如果用户请求 /user/123,FullPath 会返回路由模板 /user/:id。这能避免 Prometheus 中产生成千上万个不同的标签导致内存溢出。

处理 404 请求:当用户访问不存在的路由时,c.FullPath() 会返回空字符串。代码中加了 if path == "" { path = "unknown" } 的防御性编程,防止 Prometheus 报错。

内置 Go 运行时监控:在 Init 函数中自动注册了 prometheus.NewGoCollector()。这意味着你不仅能看到 QPS 和延迟,还能在 Grafana 里直接看到当前服务的 Goroutine 数量、内存分配 和 GC 耗时,排查内存泄漏和协程泄漏极其方便。

接口隔离:/metrics 和 /healthz 注册在 r.Use() 之前,天然免疫监控中间件,不会把基础设施的探活请求也统计进业务 QPS 中。