# Gin集成Prometheus

prometheus 是一款开源的监控和告警系统,它可以帮助你收集和存储各种指标数据,并提供查询和可视化功能。Gin 是一款高性能的 Go 语言 Web 框架,它可以用来构建高性能的 Web 应用程序。 在本教程中,我们将介绍如何在 Gin 集成 Prometheus 来收集和存储该应用程序的指标数据。

# 初始化prometheus服务

package service

import (
	"os"

	"gitea.fbbmore.com/goweb/fsdk-go/fsdkutils"
	"github.com/prometheus/client_golang/prometheus"
)

// 全局获取主机名称
var HostName, _ = os.Hostname()

var (
	// 定义一个容器名称的 Gauge
	NexcoContainer = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "nexco_container",
			Help: "Gauge of container name.",
		},
		[]string{"ip"},
	)

	// 定义一个 HTTP 请求计数器
	NexcoHttpRequestsTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "nexco_http_requests_total",
			Help: "Total number of HTTP requests processed, partitioned by status code and method.",
		},
		[]string{"method", "path", "status"},
	)

	// 定义一个 HTTP 请求处理时间的直方图
	NexcoHttpRequestDuration = prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Name:    "nexco_http_request_duration_seconds",
			Help:    "Histogram of HTTP request durations in seconds.",
			Buckets: prometheus.DefBuckets, // 可以自定义桶
		},
		[]string{"method", "path", "status"},
	)
)

func init() {
	// 定义全局标签
	podLabel := prometheus.Labels{"container": HostName}
	// 包装默认注册器,添加统一的 container 标签
	registry := prometheus.WrapRegistererWith(podLabel, prometheus.DefaultRegisterer)

	// 设置容器的ip
	NexcoContainer.WithLabelValues(fsdkutils.MustIPv4()).Set(1)

	// 注册指标
	registry.MustRegister(NexcoContainer)
	registry.MustRegister(NexcoHttpRequestsTotal)
	registry.MustRegister(NexcoHttpRequestDuration)
}
  • NexcoContainer 是一个 GaugeVec 类型的指标,它用于表示容器的名称。我们使用 WithLabelValues 方法为该指标添加了一个标签,标签的值为当前主机的 IP 地址。
  • NexcoHttpRequestsTotal 是一个 CounterVec 类型的指标,它用于表示 HTTP 请求的总数。我们使用 WithLabelValues 方法为该指标添加了三个标签,分别是请求的方法、路径和状态码。
  • NexcoHttpRequestDuration 是一个 HistogramVec 类型的指标,它用于表示 HTTP 请求的处理时间。我们使用 WithLabelValues 方法为该指标添加了三个标签,分别是请求的方法、路径和状态码。
  • 在初始化的时候,首先给这些注册器添加了一个全局标签 container,然后使用 WrapRegistererWith 方法包装了默认的注册器,这样在注册指标的时候,就会自动添加这个全局标签。
  • 最后将这些指标注册到 prometheus.DefaultRegisterer 中。这样,这些指标就可以被 Prometheus 收集和存储了。

# 上报指标数据

上面的代码中,我们定义了三个指标,但是并没有将它们的数据上报到 Prometheus。接下来,我们需要在应用程序中添加一些代码,将这些指标的数据上报到 Prometheus。

package middleware

import (
	"strconv"
	"time"

	"gitea.fbbmore.com/goweb/nexco/api/internal/service"
	"github.com/gin-gonic/gin"
)

func PrometheusMiddleware(svc *service.Service) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		start := time.Now()

		// 处理请求
		ctx.Next()

		duration := time.Since(start).Seconds()
		status := ctx.Writer.Status()
		method := ctx.Request.Method
		path := ctx.Request.URL.Path

		// 更新指标
		service.NexcoHttpRequestsTotal.WithLabelValues(method, path, strconv.Itoa(status)).Inc()
		service.NexcoHttpRequestDuration.WithLabelValues(method, path, strconv.Itoa(status)).Observe(duration)
	}
}
// 全局使用中间件
engine.Use(
    middleware.RecoveryMiddleware(svc),         // panic recovery
    middleware.PrometheusMiddleware(svc),       // prometheus监控
    middleware.TraceIdMiddleware(svc),          // 全局请求ID生成
    middleware.CorsMiddleware(svc),             // 允许跨域请求
    middleware.RequestBodyCacheMiddleware(svc), // 请求body缓存
    middleware.RequestLoggerMiddleware(svc),    // 请求日志记录
    middleware.RateLimitMiddleware(svc),        // 限流处理
)
  • 在上面的代码中,我们定义了一个 PrometheusMiddleware 中间件,它会在每个请求处理之前和之后更新指标数据。
  • 在请求处理之前,我们记录了请求开始的时间。
  • 在请求处理之后,我们记录了请求结束的时间,并计算了请求的处理时间。
  • 然后,我们使用 WithLabelValues 方法为指标添加了标签,并使用 Inc 方法更新了 NexcoHttpRequestsTotal 指标的值。这个指标表示 HTTP 请求的总数。
  • 使用 Observe 方法更新了 NexcoHttpRequestDuration 指标的值。这个指标表示 HTTP 请求的处理时间。
  • 最后,我们将这个中间件添加到 Gin 的路由中,这样每个请求都会经过这个中间件,并更新指标数据。

# 暴露指标数据

在应用程序中定义了指标之后,我们需要将这些指标暴露给 Prometheus,以便 Prometheus 可以收集和存储这些指标。在 Go 中,我们可以使用 promhttp 包来暴露指标数据。

package handler

import (
	"net/http"

	"gitea.fbbmore.com/goweb/nexco/api/internal/service"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

func init() {
	routes = append(routes, prometheusRoutes...)
}

var prometheusRoutes = []Route{
	{
		Method:  http.MethodGet,
		Path:    "/metrics",
		Handler: PrometheusHandler,
	},
}

func PrometheusHandler(svc *service.Service) gin.HandlerFunc {
	return gin.WrapH(promhttp.Handler())
}
  • 在上面的代码中,我们定义了一个 PrometheusHandler 函数,它返回一个 gin.HandlerFunc,这个函数会使用 promhttp.Handler() 函数来创建一个 HTTP 处理器,这个处理器会将指标数据暴露给 Prometheus。
  • 然后,我们将这个处理器添加到 Gin 的路由中,这样当用户访问 /metrics 路径时,Prometheus 就可以收集和存储指标数据了。