可观测性:Spring Boot Actuator & Prometheus & Grafana

  • 尽管系统健壮,但它依旧是一个“黑盒子”。当压力来临时,无法知道瓶颈在哪里,无法知道系统内部发生了什么。
  • V2.3引入可观测性,用数据说话。

配置 Spring Boot Actuator

配置过程
  • 添加Maven依赖,在pom.xml文件中添加如下内容,保存文件,并让Maven重新加载依赖。
    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  • 修改application.properties,需要告诉Actuator,允许通过Web访问Prometheus这个端点,并为所有指标打上一个统一的标签,方便后续在Grafana中筛选。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # ================== Application Info ==================
    # 定义应用名称,这个名称将被用作监控标签
    spring.application.name=seckill-system

    # ================== Actuator Configuration ==================
    # 暴露所有 Web 端点(在生产环境中应按需暴露,* 是为了开发方便)
    # 确保 prometheus 在其中
    management.endpoints.web.exposure.include=*

    # (最佳实践) 为所有从本应用出去的指标,都自动打上一个名为'application'的标签
    management.metrics.tags.application=${spring.application.name}
  • 验证:启动应用,在浏览器中访问http://localhost:8080/actuator/prometheus,有一个纯文本页面,显示了大量的指标数据。
Actuator的工作过程
  1. 自动化扫描:当添加了相关依赖并启动应用时,它会扫描项目中的“类路径”,为所有的关键组件(DB、Redis、Tomcat…)安装探针。
  2. 持续数据采集:Actuator通过Micrometer持续的从探针中读取数据,并把他们转换成一种标准格式,存在内存的一个注册表中。
  • 对于web请求:Spring Boot会自动配置一个过滤器Filter,JMeter发来的每一个HTTP请求,在进入Controller和离开之后,都会经过这个过滤器。过滤器会记录下请求的耗时、URL、HTTP状态码等信息,再喂给Micrometer。
  • 对于数据库连接池:HikariCP连接池本身就提供了获取“活跃连接数”、“空闲连接数”等状态的方法。Actuator会定期调用这些方法,更新MicroMeter中的读数。
  • 对于JVM指标:Actuator通过标准的JMX接口,从JVM获取内存使用、线程数量、GC次数等底层信息。
  1. 按需格式化输出: Micrometer 内存中已经有了所有最新的、标准格式的指标数据。现在需要把它们暴露出去,让外部系统能看到。当访问 /actuator/prometheus 时,之前加入的 micrometer-registry-prometheus 依赖开始工作。它从 Micrometer 的标准“注册表”中读取所有指标,然后将它们翻译成 Prometheus 能够理解的那种纯文本、带标签的特定格式。
  • /actuator/metrics (JSON格式): 这是一个通用的、人类可读的公告栏,它用 JSON 格式列出了所有可用的指标名称。
  • /actuator/prometheus (Prometheus格式): 这是特意开启的、一个专门给 Prometheus 看的公告栏。
  1. 被外部系统抓取:Prometheus 服务器每隔15秒,就会像一个爬虫一样,准时地来访问你应用暴露的 http://host.docker.internal:8080/actuator/prometheus 这个地址。它读取页面上所有的文本数据,解析它们,然后连同当前的时间戳一起,存入自己的时间序列数据库中。

配置 Prometheus

配置
  • 添加Maven依赖,在pom.xml文件中添加如下内容,保存文件,并让Maven重新加载依赖。
    1
    2
    3
    4
    <dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
  • 创建 prometheus.yml 配置文件,添加以下内容:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 全局配置
    global:
    scrape_interval: 15s # Prometheus 每隔 15 秒来抓取一次数据

    # 抓取配置
    scrape_configs:
    # 第一个抓取任务,命名为 'seckill-app-host'
    - job_name: 'seckill-app-host'
    # 指定去哪里抓取指标
    metrics_path: /actuator/prometheus
    # 静态配置,定义抓取的目标
    static_configs:
    # 【核心】告诉 Prometheus,目标就在你的电脑主机上
    # host.docker.internal 是一个特殊的 DNS 名称,在 Docker 容器内部会被解析为你电脑主机的 IP 地址
    - targets: ['host.docker.internal:8080']
  • 更新 docker-compose.yml 文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    services:
    # ... 已有的 redis 和 rabbitmq 服务 ...

    # 【新增】Prometheus 服务
    prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
    - "9090:9090" # 将 Prometheus 的 Web 界面暴露到主机的 9090 端口
    volumes:
    # 将我们刚创建的配置文件,挂载到容器内部
    - ./prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
    - monitor-net # 加入到我们之前定义的网络中
    # 【关键】手动为容器指路,告诉它 host.docker.internal 在哪里
    extra_hosts:
    - "host.docker.internal:host-gateway"

    # ... 你已有的 grafana 服务和 networks 定义 ...
Prometheus的工作过程
  1. 发现目标:Prometheus容器启动会,第一件事是读取并解析挂载给它的配置文件Prometheus.yml。
  2. 进行抓取:按照配置文件中的内容,每隔15秒(scrape_interval: 15s),抓取器会向配置文件中部署的http://host.docker.internal:8080/actuator/prometheus 发送一个HTTP GET请求。应用响应了这个请求,返回了一大段纯文本,充满了指标名称和数值。
  3. 数据解析与存储:Prometheus拿到这段纯文本后,解析器逐行读取进行解析,以 (指标+标签, 时间戳, 值) 的形式,高效地追加写入到它自己的时间序列数据库 (TSDB) 中。
  4. 周而复始:15秒后,Prometheus 会重复第二步和第三步,抓取新的数据,并记录下新的时间戳和值。随着时间的推移,每个指标都会在数据库中形成一条按时间排序的数据点序列,这就是“时间序列”。

配置 Grafana

配置
  • 更新 docker-compose.yml 文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    services:
    # ... 已有的 redis, rabbitmq, prometheus 服务 ...

    # 【新增】Grafana 服务
    grafana:
    image: grafana/grafana-oss:latest
    container_name: grafana
    ports:
    - "3000:3000" # 将 Grafana 的 Web 界面暴露到主机的 3000 端口
    networks:
    - monitor-net
    depends_on:
    - prometheus # 确保在 Prometheus 启动后再启动 Grafana
Grafana的工作过程
  1. 用户发起请求,http://localhost:3000/
  2. Grafana 服务端解析panel:Grafana 服务器收到请求,从自己的数据库中读取这个panel的配置,找到对应的PromQL查询。
  3. 向 Prometheus 发起Query:Grafana 服务器根据在数据源中配置的地址,向 Prometheus 服务器 (http://prometheus:9090) 发送一个 API 请求,即PromQL的查询语句。
  4. Prometheus 回答:Prometheus 收到查询后,它的 PromQL 引擎开始工作。在自己的时间序列数据库中,筛选出所有符合条件的时间序列数据。打包成一个结构化的JSON数据,并将其作为HTTP响应返回给Grafana服务器。
  5. Grafana 浏览器端展示: Grafana 服务器拿到 Prometheus 返回的 JSON 数据后,几乎原封不动地把它转发给了浏览器,浏览器中的Grafana前端开始工作,接收到这组JSON数据后,调用图形库,在panel区域将这些点连接起来,形成折线图。
  6. 自动刷新:每隔30秒,浏览器就会自动重复第二步到第五步的全过程,向 Grafana 服务器请求更新,从而拉取到最新的数据并重新绘画,让panel看起来是实时的。

启动和配置数据

  • 运行命令docker-compose up -d,启动Redis、RabbitMQ、Prometheus。Grafana这四个容器。
  • 配置 Grafana 数据
    • 登录: 访问 http://localhost:3000 (用户名/密码: admin/admin)。
    • 添加数据源:
      • 左侧菜单 ⚙️ Configuration -> Data Sources -> Add data source -> Prometheus。
      • 在 URL 处填入:http://prometheus:9090。
      • 点击 Save & test,你应该会看到绿色的成功提示。
    • 创建仪表盘:
      • 左侧菜单 + -> Dashboard -> Add a new panel。
      • 在查询区域,切换到 Code 模式。
      • 在输入框中,输入你的第一个查询语句,比如查看存活线程数。
  • 启动应用,可以在Grafana仪表盘看到如下内容。

可观测的数据信息

Web 应用层

QPS/RPS(每秒请求数)
  • 代表应用服务器每秒钟成功处理了多少个HTTP请求。
  • 衡量系统入口流量的核心指标。
  • Grafana 面板标题: API QPS (Requests/sec)
  • PromQL 语句:
    1
    2
    // 计算所有URL的总QPS
    sum(rate(http_server_requests_seconds_count{application="seckill-system"}[1m]))
  • 预期场景: 压测开始时,这条线会瞬间飙升到一个高位,反映 JMeter 施加的压力。因为我们是异步处理,这个值可以非常高。压测结束后会断崖式下跌。
API响应时间(P95延迟)
  • P95 (95th percentile) 响应时间意味着“95%的请求,其响应时间都低于这个值”。
  • 这是一个比“平均值”更能反映用户真实体验的指标。
  • Grafana 面板标题: API Response Time P95 (ms)
  • PromQL 语句:
    1
    2
    // 计算 P95 响应时间,并转换为毫秒
    histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{application="seckill-system"}[1m])) by (le)) * 1000
  • 预期场景: 由于你的 V2.x 架构是异步的,Controller 提交任务后立刻返回,所以这个值在整个压测期间应该始终保持在一个非常低的水平(比如 < 50ms)。如果它突然飙高,说明你的 Tomcat 前端线程池可能已经饱和,无法及时响应新请求。

数据库连接池

活跃数据库连接数
  • OrderConsumerService 在某一时刻,正在占用的数据库连接数量。
  • Grafana 面板标题: Active DB Connections
  • PromQL 查询: hikaricp_connections_active{application="seckill-system"}
  • 预期场景: 压测开始后,随着 RabbitMQ 消息被消费,这条线会快速上升。如果消费者并发数大于或等于连接池最大数,这条线会迅速触顶并保持在最大值,形成一条“天花板”。这条“天花板”就是数据库处理能力的明确瓶颈信号。
等待连接的线程数
  • 有多少个线程因为连接池已满,正在排队等待获取一个空闲连接。
  • Grafana 面板标题: Pending DB Connections
  • PromQL 查询: hikaricp_connections_pending{application="seckill-system"}
  • 预期场景: 在正常情况下,这个值应该永远是 0。如果它在压测时持续大于 0,这是一个非常严重的警告,说明数据库连接池已经完全饱和,成为了系统的瓶颈,大量消费者线程都在无效等待。

JVM 健康情况

JVM堆内存使用情况
  • Java 应用占用了多少内存
  • Grafana 面板标题: JVM Heap Memory Usage
  • PromQL 查询: jvm_memory_used_bytes{application="seckill-system", area="heap"}
  • 预期场景: 内存使用会随着请求处理而上升。会看到一条规律性的“锯齿状”曲线,每次下降都是一次垃圾回收 (GC) 的发生。只要整体趋势是平稳的,没有无限上涨,就说明内存健康。
系统CPU使用率
  • Spring Boot 应用进程占用了多少 CPU 资源
  • Grafana 面板标题: System CPU Usage
  • PromQL 查询: process_cpu_usage{application="seckill-system"}
  • 预期场景: 压测期间,CPU 使用率会显著升高。如果它长时间处于 90% 以上的高位,说明 CPU 可能是瓶颈之一。

业务线程池

线程池活跃线程数
  • 在某一时刻,有多少个 seckill-thread-x 正在忙碌.
  • Grafana 面板标题: App Thread Pool - Active Threads
  • PromQL 查询:
    1
    2
    // 假设线程池 Bean 名字是 seckillExecutorService
    executor_active_threads{application="seckill-system", name="seckillExecutorService"}
  • 预期场景: 压测开始后,活跃线程数会迅速上升到 corePoolSize (核心线程数),如果任务持续堆积,会进一步上升到 maximumPoolSize (最大线程数)。
线程池队列中的任务数
  • 有多少个“下单任务”正在“任务等待区”排队
  • Grafana 面板标题: App Thread Pool - Queued Tasks
  • PromQL 查询:executor_queued_tasks{application="seckill-system", name="seckillExecutorService"}
  • 预期场景: 压测开始的瞬间,这个值会迅速飙升,可能会达到设置的队列容量上限(如100)。随着后台线程的处理,这个值会逐渐下降。

一个成功的秒杀系统

正确性

  • 核心指标:
    • 绝不超卖:最终成功创建的订单数,必须小于或等于商品初始库存
    • 数据最终一致性:压测结束后,Redis中扣减的库存数、MYSQL中创建的订单数。MYSQL中最终扣减的库存数这三者必须完全相等。
  • 如何衡量:通过数据库查询来事后审计。

性能

  • 核心指标:
    • 高吞吐量(QPS/RPS):系统每秒钟能处理多少个秒杀请求,这是衡量系统容量的最核心的指标。
    • 低响应时间(P95/P99):用户点击“秒杀”后,多长时间内能得到“成功”、“失败”或“排队中”的响应。
    • 高成功率:在系统容量上限之内,所有的合法请求都应该被成功处理(即使结果是“已售罄”),而不应该服务器错误或超时而失败。
  • 如何衡量:
    • JMeter聚合报告:提供吞吐量、平均/P95响应时间、错误率等核心数据。
    • Grafana Panel:实时、图形化地展示QPS和响应时间曲线。

高可用性与稳定性

  • 核心指标:
    • 无单点故障:系统的任何一个组件宕机,都不应该导致整个服务中断。
    • 自动故障转移:在关键组件发生故障时,系统能在无人干预的情况下,在短时间内自动恢复服务。
    • 过载保护:当流量超出系统极限时,系统不应该崩溃,而应该优雅地拒绝超出的请求(限流、降级)。
  • 如何衡量:通过混沌工程的思想进行演习。

可扩展性

  • 核心指标:
    • 水平扩展能力:能否通过简单地增加机器(服务器实例),来线性的提升整个系统的处理能力。
  • 如何衡量:
    • 架构评审:分析系统是否存在“有状态”的节点,阻碍了水平扩展。
    • 压测验证扩展是否有效。

可观测性

  • 核心指标:
    • Metrics (指标): 是否有对关键组件(JVM、连接池、线程池、QPS、延迟)的实时、量化监控。
    • Logging (日志): 日志是否规范、有效,是否易于查询和分析(集中化日志)。
    • Tracing (追踪): 在微服务架构中,能否追踪一个请求在多个服务之间的完整调用链路。
  • 如何衡量:集中化监控panel。

安全性

  • 核心指标:
    • 防刷能力:能否有效识别和拦截来自脚本的、非法的、重复的高频请求。
  • 如何衡量:分析压测日志。

本站由 Xylumina 使用 Stellar 1.30.0 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本"页面"访问 次 | 总访问 次 | 总访客