Prometheus 基础概念
核心概念
指标类型
- Counter(计数器):只能增加的累积指标,用于统计请求数、错误数等
- Gauge(仪表盘):可以任意增减的指标,用于统计内存使用量、CPU使用率等
- Histogram(直方图):观察结果采样,统计数据分布,如请求延迟、响应大小等
- Summary(摘要):类似于Histogram,但提供分位数统计
时间序列
- 由指标名称和标签组合唯一标识的数据流
- 每个时间序列包含时间戳和数值
监控架构
应用程序 → Prometheus Server → Grafana/AlertManager
↓ ↓ ↓
暴露指标 收集&存储指标 可视化&告警
实现监控
下面我们先实现对HTTP请求总数(HttpRequestsTotal)这一指标的完整监控流程。
编写监控逻辑
项目初始化
mkdir prometheus-demo
cd prometheus-demo
go mod init prometheus-demo
go get github.com/gin-gonic/gin
go get github.com/prometheus/client_golang
定义HttpRequestsTotal指标
创建 main.go 文件,首先定义这个核心指标:
package main
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
// 定义HTTP请求总数指标
var httpRequestsTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "endpoint", "status"},
)
指标详解:
Name: "http_requests_total":指标名称,遵循Prometheus命名规范。Prometheus命名规范建议使用小写字母、下划线分隔单词,且以资源名_操作类型_单位(可选)为结构。Help:指标描述,说明其用途[]string{"method", "endpoint", "status"}:标签维度,用于对指标进行分类。method:HTTP方法(GET、POST等)endpoint:请求端点路径status:HTTP状态码
创建监控中间件
在main.go文件中添加中间件:
import (
"strconv"
"github.com/gin-gonic/gin"
// ... 其他导入
)
// HTTP请求监控中间件
func prometheusMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
// 不处理指标端点路径
if ctx.Request.URL.Path == "/metrics" {
// 直接跳过
ctx.Next()
return
}
// 处理请求
c.Next()
// 请求处理完成后记录指标
status := strconv.Itoa(ctx.Writer.Status())
httpRequestsTotal.WithLabelValues(
ctx.Request.Method, // HTTP方法
ctx.FullPath(), // 路由路径
status, // 状态码
).Inc() // 指标值加1
}
}
应用监控逻辑
创建简单的API端点
继续在main.go中添加业务逻辑:
import (
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
// ... 其他导入
)
func main() {
// 创建Gin路由器
r := gin.Default()
// 应用Prometheus中间件到所有路由
r.Use(prometheusMiddleware())
// 暴露指标端点
r.GET("/metrics", gin.WrapH(promhttp.Handler()))
// 业务API端点
r.GET("/api/health", func(ctx *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok"})
})
r.GET("/api/error", func(ctx *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"error": "模拟错误"})
})
// 启动服务器
r.Run(":8080")
}
验证监控效果
2.5.1 启动应用并初步验证
启动应用:
go run main.go
应用启动后,服务器会监听在8080端口。
2.5.2 生成HttpRequestsTotal监控数据
打开新的终端窗口,执行以下测试命令:
# 发送GET请求
curl http://localhost:8080/api/health
# 发送请求到错误端点(产生500状态码)
curl http://localhost:8080/api/error
# 批量发送请求
for i in {1..5}; do
curl -s http://localhost:8080/api/health
done
查看HttpRequestsTotal指标数据
查看指标端点:
curl http://localhost:8080/metrics | grep http_requests_total
应该看到类似以下输出:
# HELP http_requests_total Total number of HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="GET",endpoint="/api/health",status="200"} 6
http_requests_total{method="GET",endpoint="/api/error",status="500"} 1
这样我们就实现了对HTTP请求总数的监控,可以通过 /metrics 端点查看指标数据。
扩展到其他类型指标
我们将按照以下顺序逐步扩展:
- Gauge指标:活跃连接数监控
- Histogram指标:请求响应时间监控
- Summary指标:请求大小分位数统计
添加Gauge指标监控活跃连接数
Gauge指标特点
- 可增可减:与Counter不同,Gauge可以任意变化
- 瞬时值:表示某个时刻的状态快照
- 适用场景:内存使用量、连接数、队列长度等
定义Gauge指标
在main.go文件中添加:
var activeConnections = promauto.NewGauge(
prometheus.GaugeOpts{
Name: "active_connections",
Help: "Number of active connections",
},
)
在中间件中应用连接数监控逻辑
func prometheusMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
// 连接开始时增加计数
activeConnections.Inc()
// 确保连接结束时减少计数
defer activeConnections.Dec()
// 处理请求
c.Next()
// ...
}
}
测试
创建持续的API端点用于测试活跃连接数监控
r.GET("/api/slow", func(c *gin.Context) {
time.Sleep(30 * time.Second) // 延迟30秒
c.JSON(http.StatusOK, gin.H{"message": "慢速响应完成"})
})
在终端执行以下命令:
# 启动应用
go run main.go
# 发送并发请求测试连接数变化
for i in {1..120}; do curl -s http://localhost:8080/api/slow > /dev/null & done
# 查看活跃连接数,指标值应该为120
curl -s http://localhost:8080/metrics | grep active_connections
# 等待接口请求完成
# 再次查看活跃连接数,指标值应该为0
curl -s http://localhost:8080/metrics | grep active_connections
添加Histogram指标监控响应时间
Histogram指标特点
- 分布统计:将数据分到不同的桶(bucket)中
- 自动计算:提供计数、总和、分位数
- 适用场景:响应时间、请求大小、处理延迟等
定义Histogram指标
var httpRequestDuration = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of HTTP requests in seconds",
Buckets: prometheus.DefBuckets, // 默认桶:0.005, 0.01, 0.025, 0.05...
},
[]string{"method", "endpoint"},
)
在中间件中应用响应时间监控逻辑
func prometheusMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
start := time.Now() // 记录开始时间
// ...
c.Next()
// 计算请求处理时间
duration := time.Since(start).Seconds()
// ...
// 记录请求时间分布
httpRequestDuration.WithLabelValues(
ctx.Request.Method,
ctx.FullPath(),
).Observe(duration)
}
}
测试
创建随机响应时间的API端点用于测试响应时间监控
r.GET("/api/random-speed", func(c *gin.Context) {
var millisecond = rand.Intn(2000)
var duration = time.Duration(millisecond) * time.Millisecond
time.Sleep(duration)
c.JSON(http.StatusOK, gin.H{"message": "随机速率响应完成"})
})
# 发送一些请求
for i in {1..10}; do
curl -s http://localhost:8080/api/random-speed > /dev/null
done
# 查看响应时间统计
curl -s http://localhost:8080/metrics | grep http_request_duration
应该看到类似输出:
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="0.005"} 0
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="0.01"} 0
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="0.025"} 0
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="0.05"} 0
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="0.1"} 0
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="0.25"} 0
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="0.5"} 2
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="1"} 3
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="2.5"} 10
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="5"} 10
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="10"} 10
http_request_duration_seconds_bucket{endpoint="/api/random-speed",method="GET",le="+Inf"} 10
上面的输出表示,有2次请求的响应时间小于等于0.5秒,有3次请求的响应时间小于等于1秒,有10次请求的响应时间小于等于2.5秒,以此类推。