乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Spring Boot 概述

    • 章节导读:Spring Boot概述与核心理念
    • Spring Boot是什么
    • Spring Boot与Spring Framework的关系
    • 约定优于配置
  • 第2章 快速入门与第一个应用

    • 章节导读:快速入门与第一个应用
    • SpringApplication
    • 第一个Spring Boot应用
  • 第3章 起步依赖与版本管理

    • 章节导读:起步依赖与版本管理
    • 起步依赖
    • BOM版本管理
  • 第4章 自动配置原理

    • 章节导读:自动配置原理
    • 自动配置原理
    • AutoConfigurationImportSelector
    • 自动配置报告
  • 第5章 核心注解

    • 章节导读:核心注解
    • @SpringBootApplication
    • @EnableAutoConfiguration
    • @ConditionalOnClass
    • @ConditionalOnMissingBean
    • @ConditionalOnBean
    • @ConditionalOnProperty
  • 第6章 外部化配置与属性绑定

    • 章节导读:外部化配置与属性绑定
    • 外部化配置
    • @ConfigurationProperties
    • @Value
    • 配置属性优先级
  • 第7章 Profile 与环境切换

    • 章节导读:Profile与环境切换
    • @Profile
    • 多环境配置文件
  • 第8章 内嵌服务器与部署

    • 章节导读:内嵌服务器与部署
    • 内嵌服务器
    • Fat Jar
  • 第9章 Actuator 与监控

    • 章节导读:Actuator与监控
    • Actuator Health
    • Actuator Info
    • Actuator Metrics
    • 自定义Endpoint
    • 自定义HealthIndicator
  • 第10章 开发工具与最佳实践

    • 章节导读:开发工具与最佳实践
    • Banner自定义
    • 热部署

Actuator metrics 端点

一句话定位:Spring Boot Actuator 的 /actuator/metrics 端点通过集成 Micrometer 计量库,将 JVM 内存、线程、HTTP 请求、数据源等运行时指标统一暴露为结构化数据,是连接应用与 Prometheus、Datadog 等监控系统的数据源头。


定义与作用

在传统 Java 应用中,获取运行时指标通常有两种途径:一是通过 JMX(如 jconsole 连接 MBean),二是开发者在业务代码中手动埋点计数。JMX 对运维不够友好,手动埋点则重复劳动多、命名不规范、难以聚合。

Spring Boot Actuator 引入 Micrometer 作为指标门面(Facade),自动采集以下维度的指标:

指标命名空间采集内容自动注册条件
jvm.memory.usedJVM 已使用内存(堆/非堆)始终
jvm.gc.pause垃圾回收暂停时间与次数始终
jvm.threads.live当前存活线程数始终
http.server.requestsHTTP 请求数、耗时、结果分布存在 Web 环境
system.cpu.usage系统 CPU 使用率始终
process.uptime进程运行时长始终
jdbc.connections.active活跃数据库连接数存在 DataSource
cache.gets缓存命中/未命中次数存在 CacheManager

Micrometer 的定位:类似于 SLF4J 是日志门面,Micrometer 是指标门面。它定义了统一的指标 API(Counter、Timer、Gauge、DistributionSummary),底层可适配 Prometheus、InfluxDB、CloudWatch 等后端注册表。Spring Boot 2.x 默认将 Micrometer 与 Actuator 绑定,无需额外引入。


适用位置与常用属性

metrics 端点由 spring-boot-starter-actuator 自动引入,无需代码注解。但如要将指标推送/拉取到外部监控系统,需额外配置或引入桥接包。

常用配置属性

# application.yml
management:
  server:
    port: 8081
  endpoints:
    web:
      exposure:
        include: "health,info,metrics,prometheus"
  endpoint:
    metrics:
      enabled: true
    prometheus:
      enabled: true
  metrics:
    tags:
      application: ${spring.application.name:student-app}  # 全局标签
    distribution:
      percentiles-histogram:
        http.server.requests: true   # 暴露 HTTP 请求耗时分位直方图
      slo:
        http.server.requests: 50ms,100ms,200ms,500ms,1s,5s  # 服务等级目标
    enable:
      jvm: true
      process: true
      system: true
      tomcat: true
属性说明典型值
management.endpoints.web.exposure.include暴露的 HTTP 端点metrics,prometheus
management.metrics.tags.*为所有指标附加全局标签application=student-app
management.metrics.distribution.percentiles-histogram.*开启分位直方图true
management.metrics.distribution.slo.*定义服务等级目标边界50ms,100ms,200ms
management.metrics.enable.*按命名空间开关指标true / false

Prometheus 格式:若引入 micrometer-registry-prometheus 依赖,Actuator 会额外暴露 /actuator/prometheus 端点,返回 Prometheus 可抓取的文本格式。


核心原理

Micrometer 指标采集与暴露流程

流程解读:

  1. 应用启动时,MeterRegistry(默认 CompositeMeterRegistry)被创建。它可同时持有多个后端注册表(如 Simple、Prometheus)。
  2. Spring Boot 自动注册器(如 JvmMetrics、WebMvcMetricsFilter)向 MeterRegistry 注册各类 Meter 对象。
  3. 业务运行时,自动过滤器记录 HTTP 请求耗时,JVM 内部线程更新内存数据,自定义代码调用 counter.increment(),所有这些操作最终写入 MeterRegistry。
  4. 当外部访问 /actuator/metrics 时,MetricsEndpoint 从 MeterRegistry 读取当前快照,格式化为 JSON 返回。

指标类型与数据结构


完整示例

场景说明

飞翔科技的学生成绩管理系统上线后,架构师白歌要求小崔监控以下运行时指标:JVM 内存使用、HTTP 接口的 QPS 与平均响应时间。小崔需要确认 Actuator 的 metrics 端点能正确暴露这些数据,并验证分位直方图功能。

操作前:未引入监控指标

# 操作前:application.yml
spring:
  application:
    name: student-app
server:
  port: 8080

此时项目中没有 spring-boot-starter-actuator,访问任何 /actuator/* 均返回 404。小崔只能通过操作系统命令查看进程内存,无法精确获取 JVM 堆内存和 HTTP 请求指标。

使用该特性的完整代码

步骤一:在 pom.xml 中引入 Actuator(以及可选的 Prometheus 桥接):

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- 可选:Prometheus 格式输出 -->
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
</dependencies>

步骤二:在 application.yml 中配置指标暴露:

spring:
  application:
    name: student-app

server:
  port: 8080

management:
  server:
    port: 8081
  endpoints:
    web:
      exposure:
        include: "health,info,metrics,prometheus"
  metrics:
    tags:
      application: ${spring.application.name}
    distribution:
      percentiles-histogram:
        http.server.requests: true
      slo:
        http.server.requests: 50ms,100ms,200ms,500ms

步骤三:提供一个简单的 Controller 产生 HTTP 请求样本(仅用于验证指标,非教程重点):

package com.feixiang.student.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StudentController {

    @GetMapping("/students/{id}")
    public String getStudent(@PathVariable Long id) {
        return "学生 " + id + " 成绩查询成功";
    }
}

操作后运行结果及分析

启动应用后,先产生若干 HTTP 请求:

$ curl http://localhost:8080/students/2024001
$ curl http://localhost:8080/students/2024002
$ curl http://localhost:8080/students/2024003

访问指标列表:

$ curl http://localhost:8081/actuator/metrics

返回结果(节选):

{
    "names": [
        "jvm.memory.used",
        "jvm.memory.committed",
        "jvm.gc.pause",
        "jvm.threads.live",
        "process.uptime",
        "system.cpu.usage",
        "http.server.requests",
        "tomcat.sessions.active.current"
    ]
}

查询具体内存指标:

$ curl http://localhost:8081/actuator/metrics/jvm.memory.used

返回结果:

{
    "name": "jvm.memory.used",
    "description": "The amount of used memory",
    "baseUnit": "bytes",
    "measurements": [
        {"statistic": "VALUE", "value": 68730384}
    ],
    "availableTags": [
        {"tag": "area", "values": ["heap", "nonheap"]},
        {"tag": "id", "values": ["G1 Old Gen", "G1 Survivor Space", "CodeHeap 'non-nmethods'", "Metaspace", "CompressedClassSpace", "G1 Eden Space", "CodeHeap 'profiled methods'", "CodeHeap 'non-profiled methods'"]}
    ]
}

查询 HTTP 请求指标:

$ curl http://localhost:8081/actuator/metrics/http.server.requests

返回结果:

{
    "name": "http.server.requests",
    "description": null,
    "baseUnit": "seconds",
    "measurements": [
        {"statistic": "COUNT", "value": 3},
        {"statistic": "TOTAL_TIME", "value": 0.045}
    ],
    "availableTags": [
        {"tag": "exception", "values": ["None"]},
        {"tag": "method", "values": ["GET"]},
        {"tag": "outcome", "values": ["SUCCESS"]},
        {"tag": "status", "values": ["200"]},
        {"tag": "uri", "values": ["/students/{id}"]}
    ]
}

Prometheus 格式输出:

$ curl http://localhost:8081/actuator/prometheus | grep http_server_requests

返回结果:

http_server_requests_seconds_count{application="student-app",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/students/{id}",} 3.0
http_server_requests_seconds_sum{application="student-app",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/students/{id}",} 0.045
http_server_requests_seconds_bucket{application="student-app",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/students/{id}",le="0.05",} 2.0
http_server_requests_seconds_bucket{application="student-app",exception="None",method="GET",outcome="SUCCESS",status="200",uri="/students/{id}",le="0.1",} 3.0

分析:

  1. jvm.memory.used 自动暴露:包含 area(heap/nonheap)和 id(各内存区域)标签,可精确到具体内存池。
  2. http.server.requests 自动暴露:COUNT 为请求总数,TOTAL_TIME 为总耗时。availableTags 包含 method、status、uri 等维度,支持 Prometheus 的多维聚合查询。
  3. percentiles-histogram 生效:Prometheus 格式中出现了 seconds_bucket 直方图数据,可用于计算 P50、P95、P99 分位值。
  4. 全局标签 application=student-app 生效:所有指标自动携带应用名标签,在多应用混部环境中便于区分数据来源。

易错场景与面试考点

易错场景一:指标名称拼写错误导致 404

小崔想查看 JVM 线程数,误将指标名写错:

$ curl http://localhost:8081/actuator/metrics/jvm.thread.live

返回:

{
    "timestamp": "2024-05-20T12:00:00",
    "status": 404,
    "error": "Not Found",
    "message": "No metric named jvm.thread.live found"
}

原因:正确名称是 jvm.threads.live(threads 为复数),而非 jvm.thread.live。

正确做法:不确定指标名时,先访问 /actuator/metrics 获取完整列表,或使用通配思路搜索。Micrometer 的指标命名遵循规范(复数形式、点分隔),记住常见命名空间即可:jvm.*、process.*、system.*、http.server.requests、jdbc.connections.*。

易错场景二:未引入 Prometheus 桥接却访问 /actuator/prometheus

$ curl http://localhost:8081/actuator/prometheus

返回 404。小崔误以为 include: "prometheus" 配置就够了。

原因:prometheus 端点是额外依赖 micrometer-registry-prometheus 提供的,仅配置暴露列表不会自动创建该端点。

正确做法:POM 中引入对应桥接包:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

面试考点

Q:/actuator/metrics 和 /actuator/prometheus 有什么区别?

/actuator/metrics 返回 JSON 格式的指标列表或单个指标的详细测量值,适合人阅读和调试。/actuator/prometheus 返回 Prometheus 服务端可抓取的文本格式(含 HELP、TYPE、分桶数据),适合机器解析和时序数据库存储。

Q:Micrometer 的 Counter 和 Gauge 有什么区别?

Counter 只能单调递增,记录事件总数(如请求数、错误数),支持 increment()。Gauge 记录瞬时值,可增可减(如内存使用量、队列长度),通常通过回调函数实时获取底层值。两者不可互相替代。

Q:Spring Boot 2.7.x 如何开启 HTTP 请求耗时直方图?

在 application.yml 中设置 management.metrics.distribution.percentiles-histogram.http.server.requests: true。开启后,Micrometer 会自动按桶边界(默认或自定义 SLO)记录分布,Prometheus 输出中会出现 *_bucket 系列数据。

Q:为什么 JVM 内存指标有 area 和 id 两个标签?

area 区分大区域(heap 堆内存 vs nonheap 非堆内存),id 精确到具体内存池(如 G1 Eden Space、Metaspace)。这种层级标签设计让监控查询既可以看整体,也可以下钻到具体区域。

上一页
Actuator Info
下一页
自定义Endpoint