在这里插入图片描述

👋 大家好,欢迎来到我的技术博客!
📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。
🎯 本文将围绕Prometheus这个话题展开,希望能为你带来一些启发或实用的参考。
🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!


Prometheus - 监控微服务:Spring Boot 应用指标暴露与监控

在现代微服务架构中,可观测性(Observability)已成为保障系统稳定性和可维护性的核心支柱之一。随着服务数量激增、调用链路复杂化,传统的日志排查方式已难以满足快速定位问题的需求。此时,指标监控(Metrics Monitoring) 作为可观测性的三大支柱之一(另外两个是日志和追踪),扮演着至关重要的角色。

Prometheus 作为 CNCF(Cloud Native Computing Foundation)毕业的开源监控系统,凭借其强大的多维数据模型、灵活的查询语言 PromQL、高效的时序数据库以及活跃的社区生态,已成为云原生时代事实上的监控标准。而 Spring Boot 作为 Java 生态中最流行的微服务框架,天然支持与 Prometheus 的集成。

本文将深入探讨如何在 Spring Boot 应用中暴露应用指标,并通过 Prometheus 实现高效、可扩展的监控体系。我们将从基础概念入手,逐步构建完整的监控链路,涵盖自动指标采集、自定义业务指标、告警配置、可视化展示等关键环节,并辅以大量可运行的 Java 代码示例。


为什么需要监控 Spring Boot 微服务?

在微服务架构下,一个用户请求可能穿越数十个服务,任何一个环节的性能下降或故障都可能导致整个用户体验受损。如果没有有效的监控手段,排查问题将如同“盲人摸象”。

🔍 常见痛点

  • 服务不可用却无感知:服务宕机或响应超时,但运维团队未及时收到通知。
  • 性能瓶颈难以定位:CPU 或内存使用率飙升,但无法快速判断是哪个接口或哪段代码导致。
  • 资源浪费严重:过度配置资源(如线程池、连接池)造成成本上升,或配置不足导致服务雪崩。
  • 缺乏业务视角:仅关注基础设施指标(如 CPU、内存),忽略关键业务指标(如订单创建成功率、支付失败率)。

📊 监控的价值

  • 实时告警:在问题发生前或初期阶段发出预警,减少 MTTR(平均修复时间)。
  • 性能分析:通过历史指标趋势分析,识别性能瓶颈并优化代码。
  • 容量规划:基于资源使用趋势预测未来需求,合理扩容或缩容。
  • 业务洞察:将技术指标与业务指标结合,为产品决策提供数据支持。

💡 提示:Prometheus 官方文档详细介绍了其架构和设计理念,建议读者在深入实践前阅读 Prometheus 官方文档


Prometheus 核心概念速览

在动手之前,我们需要理解 Prometheus 的几个核心概念:

📈 时序数据(Time Series)

Prometheus 将所有监控数据存储为时序数据,即按时间戳组织的数值序列。每条时序由 指标名称(Metric Name) 和一组 标签(Labels) 唯一标识。

例如:

http_requests_total{method="POST", status="200", service="order-service"} 150
  • http_requests_total 是指标名称。
  • {method="POST", status="200", service="order-service"} 是标签集合,用于多维切片。
  • 150 是当前时间点的值。

🕵️‍♂️ 拉取模型(Pull Model)

与传统监控系统(如 Zabbix)的推送模型不同,Prometheus 采用主动拉取(Pull) 方式从目标服务获取指标。这意味着被监控的服务必须暴露一个 HTTP 接口(通常是 /actuator/prometheus),Prometheus 定期访问该接口抓取数据。

这种设计的优势包括:

  • 服务无需依赖监控系统客户端库。
  • 网络拓扑更清晰,便于防火墙配置。
  • 支持服务发现(Service Discovery),动态发现新服务。

🔧 PromQL:强大的查询语言

Prometheus 提供了名为 PromQL(Prometheus Query Language) 的函数式查询语言,支持聚合、过滤、数学运算等操作。例如:

# 查询过去5分钟内每秒HTTP请求数(速率)
rate(http_requests_total[5m])

# 查询内存使用率超过80%的实例
(instance_memory_usage_bytes / instance_memory_total_bytes) > 0.8

📣 Alertmanager:告警管理

当指标满足特定条件时,Prometheus 可触发告警,并将告警发送给 Alertmanager。Alertmanager 负责去重、分组、静默,并通过邮件、Slack、Webhook 等方式通知相关人员。


在 Spring Boot 中暴露 Prometheus 指标

Spring Boot 2.x 起原生支持 Micrometer,这是一个 vendor-neutral 的应用指标门面(类似 SLF4J 之于日志)。Micrometer 提供了统一的 API,可将指标输出到 Prometheus、Datadog、InfluxDB 等多种后端。

🛠️ 步骤一:添加依赖

pom.xml 中添加以下依赖:

<dependencies>
    <!-- Spring Boot Actuator:提供生产就绪功能 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!-- Micrometer Prometheus Registry -->
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
</dependencies>

⚠️ 注意:Spring Boot 2.3+ 已将 Micrometer 作为默认依赖,但需显式引入 micrometer-registry-prometheus 才能启用 Prometheus 支持。

🌐 步骤二:启用 Prometheus 端点

application.yml 中配置 Actuator 端点:

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus  # 暴露 prometheus 端点
  endpoint:
    prometheus:
      enabled: true
  metrics:
    tags:
      application: ${spring.application.name}  # 为所有指标添加 application 标签

启动应用后,访问 http://localhost:8080/actuator/prometheus,你将看到类似如下的输出:

# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{application="order-service",area="heap",id="G1 Eden Space",} 1.23456789E8
jvm_memory_used_bytes{application="order-service",area="heap",id="G1 Old Gen",} 2.3456789E8
...
# HELP http_server_requests_seconds Timer of HTTP server requests
# TYPE http_server_requests_seconds summary
http_server_requests_seconds_count{application="order-service",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/api/orders",} 42
http_server_requests_seconds_sum{application="order-service",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/api/orders",} 1.234

这些指标由 Micrometer 自动收集,涵盖 JVM、Tomcat、HTTP 请求等多个维度。


自动收集的指标详解

Micrometer 为 Spring Boot 应用自动收集了大量开箱即用的指标。以下是几类关键指标:

🖥️ JVM 指标

  • jvm_memory_used_bytes:JVM 内存使用量(堆/非堆)。
  • jvm_gc_pause_seconds:GC 暂停时间。
  • jvm_threads_live:活跃线程数。
  • jvm_classes_loaded:已加载类数量。

这些指标对诊断内存泄漏、GC 问题至关重要。

🌐 Web 指标

  • http_server_requests_seconds:HTTP 请求处理时间(直方图类型)。
    • _count:请求总数。
    • _sum:请求总耗时。
    • 可通过 rate(http_server_requests_seconds_count[5m]) 计算 QPS。
  • tomcat_sessions_active_current:当前活跃会话数(若使用 Tomcat)。

📦 缓存与数据源指标

  • cache_gets_total:缓存命中/未命中次数。
  • hikaricp_connections_active:HikariCP 连接池活跃连接数。

📚 更多自动指标列表可参考 Micrometer 官方文档 - Spring Boot Support


自定义业务指标

自动指标虽好,但往往无法满足特定业务场景的需求。例如,你可能希望监控:

  • 每小时成功创建的订单数。
  • 支付失败率。
  • 用户登录尝试次数。

Micrometer 提供了丰富的 API 来定义自定义指标。

📏 指标类型

Micrometer 支持四种基本指标类型:

类型 说明 适用场景
Counter 单调递增计数器 错误次数、请求总数
Gauge 可增可减的瞬时值 当前队列长度、内存使用量
Timer 记录事件耗时 方法执行时间、HTTP 请求延迟
Distribution Summary 记录事件大小分布 响应体大小、消息队列长度

✨ 示例:监控订单创建

假设我们有一个订单服务,希望跟踪成功和失败的订单创建次数。

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private final Counter orderCreatedSuccess;
    private final Counter orderCreatedFailure;

    public OrderService(MeterRegistry meterRegistry) {
        // 创建两个计数器,带标签区分成功/失败
        this.orderCreatedSuccess = Counter.builder("orders_created_total")
                .description("Total number of successfully created orders")
                .tag("status", "success")
                .register(meterRegistry);

        this.orderCreatedFailure = Counter.builder("orders_created_total")
                .description("Total number of failed order creations")
                .tag("status", "failure")
                .register(meterRegistry);
    }

    public void createOrder(OrderRequest request) {
        try {
            // 业务逻辑:保存订单
            saveOrder(request);
            orderCreatedSuccess.increment(); // 成功,计数+1
        } catch (Exception e) {
            orderCreatedFailure.increment(); // 失败,计数+1
            throw e;
        }
    }

    private void saveOrder(OrderRequest request) {
        // 模拟数据库操作
    }
}

访问 /actuator/prometheus,你将看到:

# HELP orders_created_total Total number of successfully created orders
# TYPE orders_created_total counter
orders_created_total{application="order-service",status="success",} 5.0
orders_created_total{application="order-service",status="failure",} 2.0

⏱️ 示例:记录方法执行时间

使用 Timer 记录关键方法的执行时间:

import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Service;

@Service
public class PaymentService {

    private final Timer paymentProcessingTimer;

    public PaymentService(MeterRegistry meterRegistry) {
        this.paymentProcessingTimer = Timer.builder("payment_processing_duration_seconds")
                .description("Time taken to process a payment")
                .register(meterRegistry);
    }

    public PaymentResult processPayment(PaymentRequest request) {
        return paymentProcessingTimer.recordCallable(() -> {
            // 模拟支付处理
            Thread.sleep(200);
            return new PaymentResult("SUCCESS");
        });
    }
}

Prometheus 将自动记录 _count_sum 以及分位数(如果配置了 histogram)。

📊 示例:动态 Gauge(如队列长度)

Gauge 通常用于反映当前状态。例如,监控一个工作队列的长度:

import io.micrometer.core.instrument.Gauge;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

@Component
public class TaskQueue {

    private final BlockingQueue<Task> queue = new LinkedBlockingQueue<>();

    public TaskQueue(MeterRegistry meterRegistry) {
        // Gauge 会定期调用 supplier 获取当前值
        Gauge.builder("task_queue_size", queue, BlockingQueue::size)
                .description("Current number of tasks in the queue")
                .register(meterRegistry);
    }

    public void addTask(Task task) {
        queue.offer(task);
    }

    public Task takeTask() throws InterruptedException {
        return queue.take();
    }
}

💡 最佳实践:避免在 Gauge 的 supplier 中执行耗时操作,因为它会在每次 scrape 时被调用。


配置 Prometheus 抓取 Spring Boot 指标

现在,我们的 Spring Boot 应用已暴露指标,下一步是配置 Prometheus 来抓取它们。

📄 prometheus.yml 配置

创建 prometheus.yml 文件:

global:
  scrape_interval: 15s  # 默认抓取间隔

scrape_configs:
  - job_name: 'spring-boot-apps'
    # 使用 DNS SRV 记录或静态配置
    static_configs:
      - targets: ['host.docker.internal:8080']  # 本地开发时指向宿主机
        labels:
          group: 'backend'

🐳 Docker 用户注意:在容器中运行 Prometheus 时,localhost 指向容器自身。若 Spring Boot 应用在宿主机上,需使用 host.docker.internal(Mac/Windows)或宿主机 IP(Linux)。

🚀 启动 Prometheus

使用 Docker 快速启动 Prometheus:

docker run -d \
  --name=prometheus \
  -p 9090:9090 \
  -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus

访问 http://localhost:9090,进入 Prometheus Web UI。

Status > Targets 页面,你应该看到 spring-boot-apps 任务状态为 UP

🔍 查询指标

在 Prometheus 查询框中输入:

# 查看所有订单创建指标
orders_created_total

# 计算过去5分钟的订单创建速率(每秒)
rate(orders_created_total[5m])

# 计算支付成功率(成功 / (成功 + 失败))
rate(orders_created_total{status="success"}[5m]) 
/ 
ignoring(status) group_left 
(rate(orders_created_total[5m]))

📌 提示:PromQL 的 ignoringgroup_left 用于处理标签不匹配的向量匹配,详见 Prometheus 向量匹配文档


构建完整的监控架构

在生产环境中,单个 Prometheus 实例可能不足以应对大规模微服务集群。我们需要考虑高可用、长期存储、告警等高级特性。

🧩 架构组件

一个典型的 Prometheus 监控栈包含以下组件:

Exposes /actuator/prometheus

Scrapes Metrics

Sends Alerts

Notifies via Email/Slack

Stores Time Series

Remote Write

Queries

Visualizes Dashboards

Spring Boot App

Prometheus Server

Alertmanager

On-Call Engineer

Local TSDB

Thanos/Cortex for Long-Term Storage

Grafana

User

  • Prometheus Server:核心组件,负责抓取、存储、查询。
  • Alertmanager:处理告警路由和通知。
  • Grafana:可视化面板,提供比 Prometheus 更丰富的图表。
  • Thanos/Cortex:用于跨集群聚合和长期存储(可选)。

📣 配置 Alertmanager

首先,在 prometheus.yml 中配置 Alertmanager 地址:

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

然后定义告警规则(alert.rules.yml):

groups:
- name: example
  rules:
  - alert: HighOrderFailureRate
    expr: rate(orders_created_total{status="failure"}[5m]) / rate(orders_created_total[5m]) > 0.1
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "High order failure rate on {{ $labels.instance }}"
      description: "Order failure rate is above 10% for more than 2 minutes."

prometheus.yml 中加载规则文件:

rule_files:
  - "alert.rules.yml"

启动 Alertmanager(使用默认配置即可):

docker run -d --name=alertmanager -p 9093:9093 prom/alertmanager

当订单失败率持续 2 分钟超过 10%,Prometheus 会触发告警并发送给 Alertmanager。


使用 Grafana 可视化指标

虽然 Prometheus 自带简单的图形界面,但 Grafana 提供了更强大、更美观的可视化能力。

🎨 启动 Grafana

docker run -d --name=grafana -p 3000:3000 grafana/grafana

访问 http://localhost:3000,默认账号密码为 admin/admin

🔗 添加 Prometheus 数据源

  1. 进入 Configuration > Data Sources
  2. 点击 Add data source,选择 Prometheus
  3. URL 填写 http://host.docker.internal:9090(根据你的环境调整)。
  4. 点击 Save & Test

📊 创建仪表盘

点击 Create > Dashboard,添加一个 Panel:

  • Query:输入 rate(orders_created_total[5m])
  • Legend{{status}}
  • Visualization:选择 Time series

你将看到成功和失败订单的实时速率曲线。

🌐 Grafana 官方提供了大量社区贡献的仪表盘模板,例如 Spring Boot Dashboard,可直接导入使用。


高级技巧与最佳实践

🏷️ 标签(Labels)设计原则

标签是 Prometheus 多维数据模型的核心,但滥用会导致高基数问题(High Cardinality),消耗大量内存。

错误示例

// 不要将用户ID、请求ID等高基数字段作为标签!
Counter.builder("user_login_attempts")
       .tag("user_id", userId) // ❌ 高基数!
       .register(registry);

正确做法

  • 仅使用低基数、有聚合意义的标签,如 statusregionservice
  • 对于高基数数据,考虑使用日志系统(如 ELK)或分布式追踪(如 Jaeger)。

📉 Histogram vs Summary

Micrometer 默认使用 Histogram 来记录 Timer 和 Distribution Summary。Histogram 在客户端预计算分位数桶(buckets),而 Summary 在服务端计算精确分位数。

Histogram 优势

  • 支持跨实例聚合(sum(rate(...)))。
  • 存储开销固定(由 bucket 数量决定)。

配置自定义 buckets

Timer.builder("payment_processing_duration_seconds")
     .publishPercentiles(0.5, 0.95, 0.99) // 客户端分位数(不推荐跨实例聚合)
     .publishPercentileHistogram()         // 发布 histogram buckets
     .serviceLevelObjectives(Duration.ofMillis(100), Duration.ofMillis(500)) // SLO buckets
     .register(meterRegistry);

application.yml 中全局配置:

management:
  metrics:
    distribution:
      percentiles-histogram:
        http.server.requests: true  # 为 HTTP 请求启用 histogram
      slo:
        http.server.requests: 100ms, 500ms, 1s  # 定义 SLO buckets

🔄 动态刷新配置

在 Kubernetes 环境中,服务实例动态变化。Prometheus 支持多种服务发现机制:

  • Kubernetes SD:自动发现 Pod、Service。
  • Consul SD:通过 Consul 服务注册中心发现。
  • DNS SD:通过 DNS SRV 记录发现。

例如,Kubernetes 配置片段:

scrape_configs:
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)

只需在 Pod 的 annotation 中添加:

annotations:
  prometheus.io/scrape: "true"
  prometheus.io/path: "/actuator/prometheus"
  prometheus.io/port: "8080"

故障排查常见问题

❓ 问题1:Prometheus 抓取目标状态为 DOWN

可能原因

  • 网络不通(防火墙、容器网络)。
  • Spring Boot 应用未暴露 /actuator/prometheus
  • 应用未启动或端口错误。

排查步骤

  1. 在 Prometheus 容器内执行 curl http://target:port/actuator/prometheus
  2. 检查 Spring Boot 日志是否有 Actuator 端点注册信息。
  3. 确认 management.endpoints.web.exposure.include 包含 prometheus

❓ 问题2:指标缺失或标签不全

可能原因

  • Micrometer 未正确注册指标。
  • 自定义指标作用域错误(如局部变量导致 GC)。

解决方案

  • 确保指标对象是成员变量(而非方法内局部变量)。
  • 使用 MeterRegistry.find() 调试指标是否存在。
// 调试:检查指标是否注册
List<Meter> meters = meterRegistry.find("orders_created_total").meters();
System.out.println("Found " + meters.size() + " meters");

❓ 问题3:高内存使用

可能原因

  • 高基数标签导致时序数量爆炸。
  • Prometheus 保留时间过长。

解决方案

  • 使用 tsdb 命令分析时序基数:
    docker exec prometheus tsdb analyze /prometheus
    
  • prometheus.yml 中限制保留时间:
    global:
      retention_time: 15d  # 仅保留15天数据
    

总结与展望

通过本文,我们系统地学习了如何在 Spring Boot 微服务中集成 Prometheus 监控:

  1. 基础集成:通过 Actuator 和 Micrometer 暴露自动指标。
  2. 自定义指标:使用 Counter、Timer、Gauge 等 API 监控业务逻辑。
  3. Prometheus 配置:抓取指标、定义告警规则。
  4. 可视化与告警:使用 Grafana 展示数据,Alertmanager 发送通知。
  5. 最佳实践:避免高基数、合理设计标签、利用服务发现。

监控不是一次性工程,而是一个持续迭代的过程。随着业务发展,你需要不断调整指标、优化告警策略、完善仪表盘。

🔮 未来方向

  • OpenTelemetry 集成:OpenTelemetry 正在统一指标、日志、追踪三大支柱。Spring Boot 3 已开始支持 OpenTelemetry。
  • Serverless 监控:在 AWS Lambda、Azure Functions 等无服务器环境中,指标暴露方式有所不同。
  • AIOps:结合机器学习,实现异常检测、根因分析等智能运维能力。

🌟 最后建议:不要为了监控而监控。始终围绕业务目标和 SLO(Service Level Objectives)来设计你的监控体系。正如 Google SRE 书中所言:“监控系统应该回答‘系统是否正常?’这个问题。

Happy Monitoring! 🚀


🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐