Prometheus

为了实时监控、查看服务的运行情况,服务挂掉后可以立刻收到预警,可以引入服务监控

Prometheus 简介

通常服务监控的流程如下:

  • 先搭个监控服务端
  • 各被监控客户端往服务端 push 数据(或服务端定时主动去客户端 pull)
  • 服务端把 pull 的数据存到时序数据库
  • 再搭建一个图形面板 Grafana 展示收集的监控数据

Prometheus 是一款开源的时序数据库和监控系统,具有以下特点:

  • 多维度数据模型:通过指标名称和键值对标签标识数据。
  • 强大的查询语言 PromQL:支持实时聚合和计算。
  • 自治架构:不依赖分布式存储,单个节点即可运行。
  • 拉取模型:主动从目标服务拉取指标(也支持推送网关)。
  • 可视化:原生支持 Grafana 集成。

架构

整个 Prometheus 从左到右可以大概分为以下四个部分:

  • NodeExporter 业务数据源

负责收集目标对象(如 Host 或 Container )的性能数据,并通过 HTTP 接口供 Prometheus Server 获取。每一个客户端都会提供一个 /metrics 的 get 接口

  • Prometheus 服务器

负责从客户端(Exporters)拉取和存储监控数据,并给用户通过 PromQL 查询

  • AlertManager 报警管理器

负责根据告警规则和预定义的告警方式发出例如Email、Webhook之类的告警。

  • 可视化监控界面

Prometheus 收集到数据之后,由 WebUI 界面进行可视化图标展示。目前我们可以通过自定义的 API 客户端进行调用数据展示,也可以直接使用 Grafana 解决方案来展示。

收集数据、处理数据、可视化展示,再进行数据分析进行报警处理

核心概念

概念说明
Metric(指标)监控目标的数据指标(如 http_requests_total)。
Label(标签)指标的维度标识(如 path="/api")。
Job一组监控目标的集合(如 job: im-server)。

快速开始

客户端

  1. 设置 Node Exporter 节点所在机器 在 Go 项目中引入 Prometheus,只需要编写一个带 /metrics 端口的服务,如:

     1package main
     2
     3import (
     4	"net/http"
     5	"github.com/prometheus/client_golang/prometheus/promhttp"
     6)
     7
     8func main() {
     9	// 业务路由
    10	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    11		w.Write([]byte("Hello, Prometheus!"))
    12	})
    13
    14	// Prometheus 指标路由
    15	http.Handle("/metrics", promhttp.Handler())
    16
    17	// 启动服务
    18	http.ListenAndServe(":8889", nil)
    19}
    
  2. 启动后访问 http://localhost:8889/metrics,可以看到默认的 Go 运行时指标(如内存、GC 等),类似:

  3. 添加自定义业务指标

     1package main
     2
     3import (
     4	"net/http"
     5	"github.com/prometheus/client_golang/prometheus"
     6	"github.com/prometheus/client_golang/prometheus/promhttp"
     7)
     8
     9// 定义自定义指标
    10var (
    11	requestCount = prometheus.NewCounter(
    12		prometheus.CounterOpts{
    13			Name: "http_requests_total",
    14			Help: "Total number of HTTP requests",
    15		},
    16	)
    17)
    18
    19func init() {
    20	// 注册自定义指标
    21	prometheus.MustRegister(requestCount)
    22}
    23
    24func main() {
    25	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    26		requestCount.Inc() // 每次请求增加计数器
    27		w.Write([]byte("Hello, Prometheus!"))
    28	})
    29
    30	http.Handle("/metrics", promhttp.Handler())
    31	http.ListenAndServe(":8080", nil)
    32}
    

    测试:

    1. 重启服务,多次访问 http://localhost:8080/
    2. 查看 http://localhost:8080/metrics,会发现 http_requests_total 指标值增长。

示例:监控请求延迟(Histogram)

 1var (
 2	requestDuration = prometheus.NewHistogram(
 3		prometheus.HistogramOpts{
 4			Name:    "http_request_duration_seconds",
 5			Help:    "Duration of HTTP requests",
 6			Buckets: prometheus.LinearBuckets(0.1, 0.1, 10), // 桶配置
 7		},
 8	)
 9)
10
11func init() {
12	prometheus.MustRegister(requestDuration)
13}
14
15// 在请求处理中记录时间
16func handler(w http.ResponseWriter, r *http.Request) {
17	start := time.Now()
18	defer func() {
19		requestDuration.Observe(time.Since(start).Seconds())
20	}()
21	// ... 业务逻辑
22}

服务端

  1. 安装运行 Prometheus 服务端 在 官网 找到最新版本的软件包,下载后解压可以得到如下文件:

  2. 编辑配置文件

     1global:
     2  scrape_interval: 15s # 默认情况下,每 15s 采集一次目标数据
     3
     4scrape_configs:
     5  - job_name: "prometheus"    # 监控自己
     6    # Override the global default and scrape targets from this job every 5 seconds.
     7    scrape_interval: 5s
     8    static_configs:
     9      - targets: ["localhost:9090"]
    10  - job_name: "test"
    11    static_configs:
    12      - targets: ["localhost:8889"] # 设置为客户端配置的端口
    
  3. 执行 ./prometheus --config.file=prometheus.yml,可以启动服务端

 1time=2025-07-03T16:36:19.936+08:00 level=INFO source=main.go:1544 msg="updated GOGC" old=100 new=75
 2time=2025-07-03T16:36:19.939+08:00 level=INFO source=main.go:676 msg="Leaving GOMAXPROCS=16: CPU quota undefined" component=automaxprocs
 3time=2025-07-03T16:36:19.940+08:00 level=INFO source=memlimit.go:198 msg="GOMEMLIMIT is updated" component=automemlimit package=github.com/KimMachineGun/automemlimit/memlimit GOMEMLIMIT=15164330803 previous=9223372036854775807
 4time=2025-07-03T16:36:19.941+08:00 level=INFO source=main.go:718 msg="No time or size retention was set so using the default time retention" duration=15d
 5time=2025-07-03T16:36:19.941+08:00 level=INFO source=main.go:769 msg="Starting Prometheus Server" mode=server version="(version=3.5.0-rc.0, branch=HEAD, revision=31f0c7007e7187b706da03e05aeeb303101164f8)"
 6time=2025-07-03T16:36:19.941+08:00 level=INFO source=main.go:774 msg="operational information" build_context="(go=go1.24.4, platform=windows/amd64, user=root@cfc05279aa30, date=20250630-13:26:33, tags=builtinassets)" host_details=(windows) fd_limits=N/A vm_limits=N/A
 7time=2025-07-03T16:36:19.951+08:00 level=INFO source=web.go:656 msg="Start listening for connections" component=web address=0.0.0.0:9090
 8time=2025-07-03T16:36:19.952+08:00 level=INFO source=main.go:1288 msg="Starting TSDB ..."
 9time=2025-07-03T16:36:19.952+08:00 level=INFO source=tls_config.go:347 msg="Listening on" component=web address=[::]:9090
10time=2025-07-03T16:36:19.952+08:00 level=INFO source=tls_config.go:350 msg="TLS is disabled." component=web http2=false address=[::]:9090
11time=2025-07-03T16:36:19.955+08:00 level=INFO source=head.go:657 msg="Replaying on-disk memory mappable chunks if any" component=tsdb
12time=2025-07-03T16:36:19.955+08:00 level=INFO source=head.go:744 msg="On-disk memory mappable chunks replay completed" component=tsdb duration=0s
13time=2025-07-03T16:36:19.955+08:00 level=INFO source=head.go:752 msg="Replaying WAL, this may take a while" component=tsdb
14time=2025-07-03T16:36:19.962+08:00 level=INFO source=head.go:825 msg="WAL segment loaded" component=tsdb segment=0 maxSegment=3 duration=6.689ms
15time=2025-07-03T16:36:19.963+08:00 level=INFO source=head.go:825 msg="WAL segment loaded" component=tsdb segment=1 maxSegment=3 duration=515.3µs
16time=2025-07-03T16:36:19.965+08:00 level=INFO source=head.go:825 msg="WAL segment loaded" component=tsdb segment=2 maxSegment=3 duration=1.0233ms
17time=2025-07-03T16:36:19.965+08:00 level=INFO source=head.go:825 msg="WAL segment loaded" component=tsdb segment=3 maxSegment=3 duration=511.2µs
18time=2025-07-03T16:36:19.965+08:00 level=INFO source=head.go:862 msg="WAL replay completed" component=tsdb checkpoint_replay_duration=0s wal_replay_duration=9.2486ms wbl_replay_duration=0s chunk_snapshot_load_duration=0s mmap_chunk_replay_duration=0s total_replay_duration=9.771ms
19time=2025-07-03T16:36:19.967+08:00 level=INFO source=main.go:1309 msg="filesystem information" fs_type=unknown
20time=2025-07-03T16:36:19.967+08:00 level=INFO source=main.go:1312 msg="TSDB started"
21time=2025-07-03T16:36:19.967+08:00 level=INFO source=main.go:1497 msg="Loading configuration file" filename=prometheus.yml
22time=2025-07-03T16:36:19.975+08:00 level=INFO source=main.go:1537 msg="Completed loading of configuration file" db_storage=0s remote_storage=0s web_handler=0s query_engine=0s scrape=7.6932ms scrape_sd=0s notify=0s notify_sd=0s rules=0s tracing=0s filename=prometheus.yml totalDuration=7.6932ms
23time=2025-07-03T16:36:19.975+08:00 level=INFO source=main.go:1273 msg="Server is ready to receive web requests."
24time=2025-07-03T16:36:19.975+08:00 level=INFO source=manager.go:176 msg="Starting rule manager..." component="rule manager"
  1. 查询监控数据

    配置完 Prometheus 读取的数据源之后,Prometheus 便可以从 Node Exporter 获取到数据了。Prometheus UI 是 Prometheus 内置的一个可视化管理界面,我们通过 http://localhost:9090 就可以访问到该页面。在控制台输入查询语句,即可获取对应的数据。默认每隔15秒会从客户端拉取一次数据。

Grafana 可视化

  1. 下载 Grafana for Windows

  2. 启动 Grafana:

    1cd C:\grafana\bin
    2.\grafana-server.exe
    
  3. 访问 http://localhost:3000(默认账号 admin/admin)。

  4. 添加 Prometheus 数据源(URL: http://localhost:9090)。

  5. 导入或自定义仪表盘。

扩展

常用指标类型

类型用途示例
Counter只增不减的计数器http_requests_total
Gauge可增减的数值(如内存使用量)memory_usage_bytes
Histogram统计分布(如响应时间分布)http_request_duration_seconds
Summary类似 Histogram,但计算分位数在客户端request_latency_seconds
  1. Counter 常用于:

    • 记录不同的API的请求数量

    • 记录kafka消费的不同类型消息数量

    • 记录业务错误码触发数量

  2. Gauge 常用于:

    • 记录服务的内存的占用

    • 记录服务CPU的占用

    • 记录队列的长度

  3. Histogram 常用于:

    • 比如我们的API服务的请求耗时,在所有的bucket分布情况。
    • 比如我们消费者处理某个事件的耗时,在所有的bucket分布情况。
  4. Summaries 常用于:

    • API服务的请求耗时,大部分是落在哪个区间

生产建议

  1. 指标命名规范:使用 _total_seconds 等后缀(官方约定)。

  2. 标签(Labels):通过标签区分不同维度的数据(如按路由或状态码):

    1requestCount = prometheus.NewCounterVec(
    2    prometheus.CounterOpts{
    3 	   Name: "http_requests_total",
    4 	   Help: "Total requests by path and status",
    5    },
    6    []string{"path", "status"},
    7)
    8// 使用时:
    9requestCount.WithLabelValues("/api", "200").Inc()
    
  3. 避免指标爆炸:不要滥用标签(如用户ID这种高基数标签)。

IM 实例

核心监控场景

  1. 消息处理性能监控

    • 指标示例:
      • im_message_processing_duration_seconds(消息处理耗时)
      • im_messages_sent_total(消息发送总量)
      • im_messages_delivered_total(消息投递成功量)
    • 用途:
      • 实时检测消息延迟(如 P99 延迟超过 500ms 告警)。
      • 统计消息吞吐量(QPS),评估系统负载。
  2. 在线用户与会话状态

    • 指标示例:
      • im_active_users_gauge(当前在线用户数)
      • im_sessions_active(活跃会话数)
      • im_connections_count(WebSocket/TCP 连接数)
    • 用途:
      • 发现异常连接断开(如连接数骤降可能预示网络问题)。
      • 评估服务器容量(如单机连接数接近上限时扩容)。
  3. 消息队列积压监控

    • 指标示例:
      • im_mq_pending_messages(消息队列积压数)
      • im_mq_consumer_lag_seconds(消费延迟)
    • 用途:
      • 发现消费能力不足(如 Kafka/RabbitMQ 积压告警)。
  4. API 接口健康度

    • 指标示例:
      • im_api_requests_total{path="/send", status="200"}(按接口和状态码统计)
      • im_api_failure_rate(接口错误率)
    • 用途:
      • 快速定位故障接口(如登录接口 500 错误率飙升)。

示例

  1. 在 IM 服务中暴露指标

     1import (
     2	"github.com/prometheus/client_golang/prometheus"
     3	"github.com/prometheus/client_golang/prometheus/promhttp"
     4)
     5
     6// 定义指标
     7var (
     8	messageSentCount = prometheus.NewCounterVec(
     9		prometheus.CounterOpts{
    10			Name: "im_messages_sent_total",
    11			Help: "Total number of messages sent",
    12		},
    13		[]string{"user_type"}, // 按用户类型区分(如 "bot", "human")
    14	)
    15
    16	messageProcessingTime = prometheus.NewHistogram(
    17		prometheus.HistogramOpts{
    18			Name:    "im_message_processing_duration_seconds",
    19			Help:    "Time taken to process a message",
    20			Buckets: []float64{0.01, 0.05, 0.1, 0.5, 1}, // 自定义桶
    21		},
    22	)
    23)
    24
    25func init() {
    26	prometheus.MustRegister(messageSentCount, messageProcessingTime)
    27}
    28
    29// 在消息处理逻辑中记录指标
    30func handleMessage(msg string) {
    31	start := time.Now()
    32	defer func() {
    33		messageProcessingTime.Observe(time.Since(start).Seconds())
    34	}()
    35
    36	// 处理消息逻辑...
    37	messageSentCount.WithLabelValues("human").Inc()
    38}
    39
    40// 暴露 /metrics 端点
    41http.Handle("/metrics", promhttp.Handler())
    
  2. Prometheus 配置

    1# prometheus.yml
    2scrape_configs:
    3  - job_name: "im-server"
    4    static_configs:
    5      - targets: ["im-server:8080"]  # IM 服务的地址
    
  3. 告警规则示例

     1# alert.rules.yml
     2groups:
     3- name: im-alerts
     4  rules:
     5  - alert: HighMessageLatency
     6    expr: histogram_quantile(0.99, rate(im_message_processing_duration_seconds_bucket[5m])) > 1
     7    for: 5m
     8    labels:
     9      severity: critical
    10    annotations:
    11      summary: "High message processing latency (instance {{ $labels.instance }})"
    12      description: "P99 message latency is {{ $value }}s"
    
  4. 高级应用场景

    • 消息广播性能

      • 群聊消息广播时,如何监控扩散延迟

      • 方案:

         1// 记录消息从发送到所有接收者的延迟
         2broadcastDuration := prometheus.NewSummaryVec(
         3    prometheus.SummaryOpts{
         4  	  Name: "im_broadcast_duration_seconds",
         5  	  Help: "Time for a message to reach all recipients",
         6    },
         7    []string{"group_size"},
         8)
         9
        10// 在广播完成后记录
        11broadcastDuration.WithLabelValues("50").Observe(elapsed.Seconds())
        
    • 在线状态同步

      • 问题:如何监控用户在线状态同步延迟?

      • 方案:

        1# 计算状态同步的平均延迟
        2rate(im_status_sync_duration_seconds_sum[5m]) / rate(im_status_sync_duration_seconds_count[5m])
        
    • 消息丢失检测

      • 问题:如何确保消息不丢失

      • 方案:

        1# 检查发送和投递计数是否匹配
        2(im_messages_sent_total - im_messages_delivered_total) > 0
        
    1. 生产环境建议

      • 标签设计:避免高基数标签(如 user_id),改用 user_type 等低基数维度。

      • 采样率:高频消息场景可抽样记录指标(如每 100 条消息记录 1 次)。

      • 多维度聚合:

        1# 按消息类型统计延迟
        2histogram_quantile(0.9, sum by (le, msg_type) (rate(im_message_processing_duration_seconds_bucket[5m])))
        

参考

  1. Prometheus(一):Web服务环境监控

  2. prometheus-book

  3. Prometheus 快速入门教程(一):Prometheus 快速入门

  4. 程序监控(一)Prometheus的四种类型及golang示例