Spring Boot执行器实战:基于spring-boot-metrics的应用性能监控工具

Spring Boot性能监控实战

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:《Spring Boot Metrics:深入理解与应用》介绍了Spring Boot框架中的核心组件spring-boot-metrics,它通过集成Actuator实现对应用程序运行时指标的自动采集与监控。该工具可收集HTTP请求、JVM内存、线程状态、数据库查询等关键性能数据,并提供标准化端点如/actuator/metrics和/actuator/prometheus供外部系统读取。支持与Prometheus、Grafana等主流监控平台无缝集成,实现指标可视化。开发者还可通过@Timed、@Counted等注解及MeterRegistry接口自定义业务级度量指标,全面提升系统的可观测性与可维护性。本项目经过完整测试,适用于开发、测试与生产环境下的性能分析与故障排查。

Spring Boot Metrics 全链路可观测性实战指南

你有没有遇到过这种情况:线上服务突然变慢,用户投诉接踵而至,但日志里翻来覆去就那几行“Request processed”,根本看不出问题在哪?或者某个接口 QPS 暴涨,CPU 直冲 100%,却不知道是哪个 URI 在作祟?

🤔 “要是能一眼看出每个接口的调用量、延迟分布,甚至 JVM 内存变化趋势就好了。”

别急——这正是我们今天要深入探讨的内容。 Spring Boot Metrics + Actuator + Prometheus + Grafana 这套组合拳,就是现代 Java 应用的“听诊器”和“X光机”。它不仅能告诉你“哪里疼”,还能帮你找到“病根”。

让我们从一个最简单的场景开始:你的微服务上线了,你想知道:

  • 刚刚部署后,到底有多少人在调用 /api/user
  • 平均响应时间是多少?有没有出现超时?
  • 系统内存使用是否正常?GC 频率高不高?

这些问题的答案,其实都藏在 spring-boot-starter-actuator 提供的指标中。而这一切,只需要几行配置就能搞定。


一、Metrics 是什么?为什么说它是系统的“生命体征”

在微服务架构下,系统就像一个人体,由多个器官(服务)协同工作。如果某个器官出了问题,比如心脏跳得太快或太慢,我们就需要通过心电图、血压计等设备监测它的状态。

那么,在软件世界里,谁来当这个“医生”?
答案是: Metrics(指标)

Metrics 就是你应用的“生命体征”——它们记录了从 HTTP 请求到 JVM 内存、线程数、CPU 使用率等一切运行时数据。没有这些数据,你就像是在黑暗中开车,只能靠运气避障。

而 Spring Boot 基于 Micrometer 构建了一套统一的指标抽象层,屏蔽了不同监控系统(如 Prometheus、Graphite、Datadog)之间的差异。这意味着你可以做到:

一次埋点,多端上报

无论你未来换哪家监控平台,代码几乎不用改!

@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
    return registry -> registry.config().commonTags("application", "user-service");
}

上面这段代码做了件小事:为所有指标自动加上 application=user-service 标签。但它带来的价值可不小——当你有几十个微服务时,靠这个标签就能快速区分来源,再也不用猜“这条数据是谁发的”。

四大核心指标类型,你知道怎么用吗?

类型 英文名 用途 示例
计数器 Counter 单调递增,适合统计总数 请求次数、错误数
瞬时值 Gauge 反映当前状态 内存使用量、线程数
耗时记录 Timer 统计方法执行时间 接口响应时间
分布摘要 DistributionSummary 记录事件大小分布 请求 Body 大小

举个例子,假设你要监控一个下单接口:

@PostMapping("/order")
public ResponseEntity<Order> createOrder(@RequestBody Order order) {
    Timer.Sample sample = Timer.start(meterRegistry); // 开始计时

    try {
        Order saved = orderService.save(order);
        return ResponseEntity.ok(saved);
    } finally {
        sample.stop(Timer.builder("order.create.duration") // 停止并上报
                .tag("status", "success")
                .register(meterRegistry));
    }
}

这样,每创建一个订单,就会生成一条耗时指标,你可以轻松分析 P95/P99 延迟,找出性能瓶颈。

是不是比手动写 System.currentTimeMillis() 清爽多了?😎


二、如何让 Actuator “活”起来?从依赖引入到端点暴露

你以为加个 starter 就万事大吉了?错!很多团队明明引入了 spring-boot-starter-actuator ,结果 /actuator/metrics 还是 404,一脸懵圈。

问题出在哪? 默认安全策略

Spring Boot 出于安全考虑,默认只开放 /health /info 两个基础端点。其他统统隐藏。所以你得主动“点亮”它们。

2.1 引入 actuator 起步依赖:Maven vs Gradle

这是第一步,也是最容易忽略的一环。

Maven 用户 👇
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

只要父 POM 继承自 spring-boot-starter-parent ,版本会自动对齐,无需显式指定。

Gradle 用户 👇
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
}

如果你用了 Kotlin DSL:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-actuator")
}

💡 建议 :不要单独引入 micrometer-core spring-boot-actuator-autoconfigure ,直接用 Starter 最省事,避免版本冲突。

依赖树长什么样?

跑一下命令看看:

mvn dependency:tree | grep actuator

你会看到类似输出:

[INFO] +- org.springframework.boot:spring-boot-starter-actuator:jar:3.2.0:compile
[INFO] |  +- org.springframework.boot:spring-boot-actuator-autoconfigure:jar:3.2.0:compile
[INFO] |  |  \- org.springframework.boot:spring-boot-actuator:jar:3.2.0:compile
[INFO] |  \- io.micrometer:micrometer-observation:jar:1.12.0:compile

关键模块有三个:

模块 作用
spring-boot-actuator-autoconfigure 自动装配主类,启动时扫描并注册端点
spring-boot-actuator 实现 HealthEndpoint、MetricsEndpoint 等核心功能
micrometer-* Micrometer 指标框架,负责数据建模与上报

这种分层设计实现了职责分离:上层管暴露,底层管采集。


2.2 自动装配机制揭秘:Spring Boot 是怎么“发现”你的端点的?

很多人以为加个注解就完事了,其实背后有一整套自动装配流程。

当 JVM 启动时,Spring Boot 会扫描类路径下的:

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

这个文件里列着所有自动配置类。其中就有:

org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration

一旦检测到 spring-boot-starter-actuator 存在,就会触发加载。

接下来发生的事可以用一张 Mermaid 流程图清晰表达:

flowchart TD
    A[应用启动 SpringApplication.run()] --> B{Classpath 中是否存在<br>spring-boot-starter-actuator?}
    B -->|是| C[加载 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports]
    C --> D[发现 ServletManagementContextAutoConfiguration]
    D --> E[注册 /actuator 映射的 DispatcherHandler]
    E --> F[扫描 @Endpoint 注解的 Bean]
    F --> G[创建 HealthEndpoint, MetricsEndpoint 等实例]
    G --> H[通过 @WebEndpointExtension 暴露为 HTTP 端点]
    H --> I[/actuator/metrics 可访问]

整个过程完全自动化,开发者无感完成。

关键配置类解析
类名 作用 条件注解
MetricsAutoConfiguration 初始化 MeterRegistry (默认为 CompositeMeterRegistry) @ConditionalOnClass(MeterRegistry.class)
WebMvcEndpointManagementContextConfiguration 在 Web 环境中注册 MVC 端点适配器 @ConditionalOnClass(DispatcherServlet.class)
HealthEndpointAutoConfiguration 创建 HealthIndicator 集合并组装成 HealthEndpoint @ConditionalOnEnabledEndpoint(endpoint = HealthEndpoint.class)

MetricsAutoConfiguration 为例,其简化源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Timed.class)
@EnableConfigurationProperties(MetricsProperties.class)
public class MetricsAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MeterRegistry meterRegistry() {
        return new CompositeMeterRegistry();
    }

    @Bean
    public MetricsRestTemplateCustomizer metricsRestTemplateCustomizer(
            MeterRegistry registry) {
        return new MetricsRestTemplateCustomizer(registry);
    }
}

逐行解读:

  • @Configuration(proxyBeanMethods = false) :禁用 CGLIB 代理,提升性能;
  • @ConditionalOnClass(Timed.class) :只有存在 Timed 注解才启用;
  • @EnableConfigurationProperties(MetricsProperties.class) :绑定 management.metrics.* 配置项;
  • meterRegistry() 返回一个复合注册中心,支持同时向多个后端发送数据;
  • @ConditionalOnMissingBean 表示允许用户自定义更高优先级的 MeterRegistry

这就给了你足够的扩展空间,比如替换为特定厂商的实现。


三、让 metrics 端点真正可用:暴露 + 安全控制 + 路径映射

现在依赖也加了,自动装配也走了,为啥还是看不到指标?

因为你还差最后一步: 显式暴露端点

3.1 application.yml 中的关键配置

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    metrics:
      enabled: true
    prometheus:
      enabled: true

重点来了:

  • management.endpoints.web.exposure.include :列出要暴露的端点,支持逗号分隔或通配符 *
  • management.endpoint.metrics.enabled :是否启用 metrics 端点本身;
  • 推荐生产环境 不要用 * ,而是明确列出所需端点,防止敏感信息泄露。
配置项 默认值 说明
include health,info 要暴露的端点列表
exclude 明确排除某些端点(优先级高于 include)
management.server.port 与主服务相同 可选:为管理端点指定独立端口

举个增强隔离性的例子:

server.port: 8080
management.server.port: 8081
management.server.address: 127.0.0.1

这样一来, /actuator/metrics 只能在内网通过 http://localhost:8081/actuator/metrics 访问,彻底阻断外部探测。

🔐 安全是底线,别图省事把监控端口暴露在公网!


3.2 安全加固:结合 Spring Security 控制访问权限

暴露之后怎么办?当然是上锁!

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

然后编写安全配置类:

@Configuration
@EnableWebSecurity
public class ActuatorSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers("/actuator/health", "/actuator/info").permitAll()
                .requestMatchers("/actuator/**").hasRole("ACTUATOR")
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails admin = User.withDefaultPasswordEncoder()
            .username("admin")
            .password("admin123")
            .roles("ACTUATOR")
            .build();
        return new InMemoryUserDetailsManager(admin);
    }
}

逻辑拆解:

  • /actuator/health /actuator/info 允许匿名访问,方便探针探测;
  • 其他所有 /actuator/** 路径必须携带 ACTUATOR 角色;
  • 使用 Basic Auth,适合机器通信;
  • 用户存储仅用于演示,生产环境应对接 LDAP/OAuth2。

测试一下:

curl -u admin:admin123 http://localhost:8080/actuator/metrics

返回 200 才算成功。


3.3 端点命名与 URL 映射规则,你真的懂吗?

Spring Boot 对 @Endpoint(id="xxx") 的类会自动映射为 /actuator/xxx 路径。

端点 ID 默认路径 是否可改
health /actuator/health
metrics /actuator/metrics
prometheus /actuator/prometheus
threaddump /actuator/threaddump

可以通过以下方式修改:

management:
  endpoints:
    web:
      path-mapping:
        health: status
        metrics: monitoring/metrics

此时新路径变为:

  • /actuator/status
  • /actuator/monitoring/metrics

还可以改前缀:

base-path: /manage

最终变成 /manage/metrics

自定义端点示例
@Endpoint(id = "custom-stats")
public class CustomStatsEndpoint {
    @ReadOperation
    public Map<String, Integer> showStats() {
        return Map.of("requests", 1000, "errors", 5);
    }
}

默认路径是 /actuator/custom-stats ,想改成 /actuator/stats-summary 怎么办?

management:
  endpoints:
    web:
      path-mapping:
        custom-stats: stats-summary

搞定!🎉


四、运行时验证与常见问题排查:别让配置白搭

配置写完了,怎么确认生效了?

4.1 启动日志怎么看?

看这几条关键日志:

INFO  o.s.b.a.e.web.EndpointLinksResolver     : Exposing 3 endpoint(s) beneath base path '/actuator'
INFO  o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http)
INFO  o.s.b.a.e.mvc.WebMvcEndpointHandlerMapping : Mapped "{[/actuator/metrics],methods=[GET]}" onto ...

如果有这些输出,说明:

  • 成功注册了 /actuator/metrics
  • 使用的是 v3 协议格式(Spring Boot 3.x 默认);
  • 已绑定到具体处理器方法。

如果没有?检查三点:

  1. 是否真加了 spring-boot-starter-actuator
  2. 是否被 @SpringBootApplication(scanBasePackages = ...) 扫描范围限制?
  3. 是否误加了 spring.autoconfigure.exclude=... 排除自动装配?

开启调试日志进一步诊断:

logging:
  level:
    org.springframework.boot.actuate: DEBUG

4.2 遇到 404?照着这张表一步步查!

检查项 检查方法 解决方案
1. 依赖是否引入 查看 pom.xml build.gradle 添加 starter 依赖
2. 端点是否启用 日志中是否有 Mapping 输出 设置 enabled=true
3. 是否暴露 include 是否包含 metrics 添加到 exposure.include
4. 路径是否变更 是否设置了 path-mapping 使用新路径访问
5. 是否启用了独立管理端口 management.server.port 是否设置 改为对应端口访问

特别注意:Spring Boot 2.x 和 3.x 默认协议不同。3.x 用 v3 ,2.x 用 v2 。如果客户端不兼容,需配置:

management:
  endpoints:
    web:
      exposure:
        versions: ["v3"]

4.3 用 curl 测试接口可达性(终极手段)

最直接的方法:

curl -i http://localhost:8080/actuator/metrics

预期响应头:

HTTP/1.1 200 OK
Content-Type: application/vnd.spring-boot.actuator.v3+json

响应体示例:

{
  "names": [
    "jvm.memory.max",
    "jvm.memory.used",
    "system.cpu.count",
    "http.server.requests"
  ]
}

带认证访问:

curl -u admin:admin123 http://localhost:8080/actuator/metrics

还可以查具体指标:

curl http://localhost:8080/actuator/metrics/http.server.requests

返回详细测量值和标签,用于后续分析。


五、内置指标详解:从 HTTP 到 JVM,全面掌握系统状态

Actuator 不只是给你一个 API,它还自带一堆“开箱即用”的监控指标。理解这些指标的含义和结构,是你做性能调优的前提。

5.1 HTTP 请求监控: http.server.requests 指标深度解析

这是最常用、最有价值的指标之一。

它有哪些维度?
标签名 示例值 说明
method GET, POST 请求方法
uri /api/users/{id} 路径模板(非具体参数)
status 200, 500 响应状态码
exception IllegalStateException 抛出异常类名,无则为 None
outcome SUCCESS, CLIENT_ERROR 结果分类,基于状态码划分

看个 Controller 示例:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        if (id <= 0) throw new IllegalArgumentException("Invalid ID");
        User user = userService.findById(id);
        return ResponseEntity.ok(user);
    }
}

每次请求都会生成这样的时间序列:

http_server_requests_seconds_count{method="GET",uri="/api/users/{id}",status="400",exception="IllegalArgumentException"} → 1
http_server_requests_seconds_sum{method="GET",uri="/api/users/{id}",status="200",exception="None"} → 0.045

其中 _count 是请求数, _sum 是总耗时,平均延迟 = sum / count。

想看 P95/P99?需要开启百分位统计:

management:
  metrics:
    web:
      server:
        request:
          autotime:
            enabled: true
            percentiles: 0.95,0.99
            percentile-buckets: 0.005,0.01,0.025,0.05,0.1,0.2,0.5,1.0

否则默认不会计算分位数,节省性能开销。


如何定位高延迟或频繁失败的接口?

PromQL 查询帮你搞定。

找出过去 5 分钟平均延迟最高的 Top 3 接口:

topk(3,
  sum(rate(http_server_requests_seconds_sum[5m]))
  /
  sum(rate(http_server_requests_seconds_count[5m]))
  by (uri)
)

检测高频 5xx 错误:

sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) by (uri, method)
  > 1  # 每秒超过一次

结合 Grafana,你可以做出“Top N 慢接口”面板,实时告警。

还想查异常激增?试试这个:

sum(increase(http_server_requests_seconds_count{exception!="None"}[1h])) by (exception)

如果 NullPointerException 突增,赶紧去查代码有没有漏判空!

整个请求生命周期的数据流动如下:

flowchart TD
    A[HTTP Request Arrives] --> B{DispatcherServlet}
    B --> C[Micrometer HandlerInterceptor]
    C --> D[Start Timer & Capture Tags]
    D --> E[Execute Controller Logic]
    E --> F{Exception Thrown?}
    F -- Yes --> G[Record Exception Tag]
    F -- No --> H[Set exception=None]
    G & H --> I[Stop Timer, Emit Metrics]
    I --> J[Store in MeterRegistry]
    J --> K[Exposed via /actuator/metrics]
    K --> L[Scraped by Prometheus]
    L --> M[Grafana Visualization]

Micrometer 通过拦截器织入 Spring MVC 生命周期,在请求开始和结束时采集数据,完美无侵入。


能不能关联 trace ID 实现链路追踪?

可以!但要小心“时间序列爆炸”。

Sleuth 会自动注入 traceId 到 MDC。我们可以自定义标签提供者:

@Component
public class CustomWebMvcTagsProvider implements WebMvcTagsProvider {

    @Override
    public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, 
                                 Object handler, Throwable ex) {
        String traceId = Span.current().getSpanContext().getTraceId();
        return Tags.of(
            WebMvcTags.method(request),
            WebMvcTags.uri(request, response, handler),
            WebMvcTags.status(response),
            Tag.of("trace_id", traceId != null ? traceId : "unknown")
        );
    }
}

⚠️ 注意:每个唯一 trace_id 都会产生新的时间序列,基数太高会压垮 Prometheus。

建议做法:

  • 日常监控 不要开启
  • 调试时临时采样上报;
  • 或将 trace_id 写入日志,通过 ELK 联合查询。

5.2 JVM 层面监控:内存、GC、线程一把抓

JVM 是 Java 应用的命脉。了解它的状态,等于掌握了系统的“心跳”。

内存使用情况: jvm.memory.used vs max
指标名称 类型 单位 描述
jvm_memory_used{area,id} Gauge bytes 当前已使用内存
jvm_memory_max{area,id} Gauge bytes 最大可用内存(-1 表示无限)
jvm_memory_committed{id} Gauge bytes 已提交给 JVM 的物理内存

标签说明:

  • area : heap (堆) or non-heap (非堆)
  • id : 内存池名称,如 PS Eden Space , Metaspace

例如:

jvm_memory_used{area="heap", id="PS Old Gen"} → 450MB
jvm_memory_max{area="heap", id="PS Old Gen"} → 1GB

计算老年代使用率:

jvm_memory_used{area="heap", id="PS Old Gen"}
/
jvm_memory_max{area="heap", id="PS Old Gen"}

持续接近 1.0?警惕内存泄漏!

主要内存区域对比:

区域 所属 area 用途 监控建议
PS Eden Space heap 新生代对象存放区 关注 YGC 频率
PS Survivor Space heap 存活对象过渡区 一般较小
PS Old Gen heap 长期存活对象 重点关注使用率
Metaspace non-heap 类元数据 动态扩展,防溢出
Compressed Class Space non-heap 压缩类指针 通常无需关注

合理设置 -Xms , -Xmx , -XX:MaxMetaspaceSize 是前提。


GC 暂停监控: jvm.gc.pause 指标解读

GC 直接影响用户体验。 jvm.gc.pause 是一个 Timer 指标,记录每次 GC 的耗时和次数。

标签包括:

  • action : end of minor GC or end of major GC
  • cause : 触发原因,如 Allocation Failure
  • statistic : count (次数) or total (累计时间)

示例数据:

jvm_gc_pause_seconds_count{action="end of minor GC"} → 120
jvm_gc_pause_seconds_total{action="end of major GC"} → 1.8

常用查询:

# 每分钟 Full GC 次数
rate(jvm_gc_pause_seconds_count{action="end of major GC"}[1m])

# 平均 Minor GC 暂停时间
avg(rate(jvm_gc_pause_seconds_total[1m])) / avg(rate(jvm_gc_pause_seconds_count[1m]))

理想情况:

  • Minor GC 快速完成(< 50ms),频率可高;
  • Major GC 尽量少(每天几次以内),否则可能影响 SLA。

Grafana 曲线图能清晰展示 GC 波动,帮助识别周期性长时间暂停。


线程监控与死锁预警

线程太多会导致上下文切换开销增大,甚至 OOM。

关键指标:

  • jvm_threads_live : 当前活跃线程数
  • jvm_threads_daemon : 守护线程数
  • jvm_threads_peak : 历史峰值
  • jvm_threads_states : 按状态分类(runnable, blocked, waiting)

监控线程增长:

jvm_threads_live > 200

虽然 Micrometer 不直接暴露死锁信息,但我们能自己补上:

@Scheduled(fixedDelay = 60_000)
public void checkDeadlock() {
    ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
    long[] deadlockedThreads = threadBean.findDeadlockedThreads();
    if (deadlockedThreads != null && deadlockedThreads.length > 0) {
        meterRegistry.counter("jvm_threads_deadlock_detected").increment();
        log.warn("Deadlock detected among {} threads", deadlockedThreads.length);
    }
}

定时任务每分钟检测一次,并以上报指标形式集成进告警系统。

可视化线程状态分布:

pie
    title JVM Threads State Distribution
    “Runnable” : 45
    “Blocked” : 10
    “Waiting” : 30
    “Timed Waiting” : 15

饼图有助于判断是否存在大量线程阻塞。


5.3 系统与运行环境指标:跳出 JVM 看全局

除了 JVM 内部,系统级指标也很重要。

system.cpu.usage vs process.cpu.usage ,有何区别?
指标名称 数据源 含义 单位
system.cpu.usage 操作系统 主机整体 CPU 使用率 0~1
process.cpu.usage JVM 采样 当前进程占用 CPU 时间比 float

关键差异:

  • system.cpu.usage 是瞬时值;
  • process.cpu.usage 是累计增量,需两次采样才能得出比率。

启用后者需配置:

management:
  metrics:
    enable:
      process: true

然后查询:

rate(process_cpu_usage_seconds_total[15s])

若结果为 1.2,表示该进程占用了 1.2 个 CPU 核心。

对比二者可判断:

(system_cpu_usage > 0.8) and (rate(process_cpu_usage_seconds_total[1m]) < 0.1)

主机忙但应用闲?可能是其他进程在抢资源!


文件描述符监控:防止 Too Many Open Files

Linux 对每个进程打开的文件数有限制。 file.descriptor.count 帮你提前发现问题。

相关指标:

  • process_files_opened : 自启动以来打开过的总文件数(Counter)
  • process_files_max : 允许的最大文件数(Gauge)

计算使用率:

process_files_opened / process_files_max

超过 80%?可能出现“Too many open files”错误。

解决方案:

  • 调整 ulimit -n
  • 优化连接池配置(如 DB、Redis)

服务生命周期监控:uptime 和 startup.time

最后两个重要指标:

  • application_uptime_seconds : 应用已运行时间
  • application_startup_duration_seconds : 启动耗时

它们反映服务稳定性与部署效率。

监控重启次数:

changes(application_uptime_seconds[1h]) > 100

频繁重启?可能是健康检查失败、OOM 或滚动发布引起。

同时, startup.time 帮助发现初始化缓慢的 Bean,推动冷启动优化。


六、编程访问 metrics 数据:不只是看,还要能处理

你以为 /actuator/metrics 只是用来给人看的?错!它也可以被程序消费。

6.1 获取指标列表与详情

先查有哪些指标:

curl http://localhost:8080/actuator/metrics

响应:

{
  "names": [
    "jvm.memory.max",
    "jvm.memory.used",
    "http.server.requests"
  ]
}

再查具体指标:

curl "http://localhost:8080/actuator/metrics/http.server.requests"

响应结构:

{
  "name": "http.server.requests",
  "description": "Time taken to handle requests",
  "baseUnit": "seconds",
  "measurements": [
    { "statistic": "COUNT", "value": 156.0 },
    { "statistic": "TOTAL_TIME", "value": 7.89 },
    { "statistic": "MAX", "value": 0.15 }
  ],
  "availableTags": [
    { "tag": "method", "values": ["GET", "POST"] },
    { "tag": "uri", "values": ["/api/user", "/login"] }
  ]
}

measurements 包含 COUNT、SUM、MAX; availableTags 提供多维切片能力。

Java 客户端示例:

public class MetricsClient {
    private final RestTemplate restTemplate = new RestTemplate();

    public void fetchMetricDetails(String metricName) {
        String url = "http://localhost:8080/actuator/metrics/" + metricName;
        Map<String, Object> response = restTemplate.getForObject(url, Map.class);

        System.out.println("Metric Name: " + response.get("name"));

        List<Map<String, Object>> measurements = (List<Map<String, Object>>) response.get("measurements");
        for (Map<String, Object> m : measurements) {
            System.out.printf("  %s = %.3f%n", m.get("statistic"), m.get("value"));
        }

        List<Map<String, Object>> tags = (List<Map<String, Object>>) response.get("availableTags");
        System.out.println("Available Tags:");
        for (Map<String, Object> t : tags) {
            List<String> values = (List<String>) t.get("values");
            System.out.printf("  %s -> %s%n", t.get("tag"), String.join(", ", values));
        }
    }
}

可用于构建本地采样器、巡检脚本或简易告警引擎。


6.2 多维标签的实际意义:灵活聚合与钻取

Micrometer 的灵魂在于 多维标签

传统方式要维护一堆指标:

  • http_get_200_count
  • http_post_500_count

而 Micrometer 用一个 http.server.requests 加标签搞定:

{
  "method": "GET",
  "uri": "/api/user",
  "status": "200",
  "count": 80,
  "avg_time": 0.045
}

想按状态码统计失败率?分组聚合就行!

PromQL 示例:

sum by(status) (rate(http_server_requests_seconds_count[5m]))

Grafana 饼图直观展示:

pie
    title HTTP Status Distribution
    “200 OK” : 150
    “404 Not Found” : 8
    “500 Server Error” : 6

还能动态传参过滤:

curl "http://localhost:8080/actuator/metrics/http.server.requests?tag=status:500&tag=uri:/api/order"

精准锁定问题接口。


6.3 编程方式实现本地监控与告警

封装一个采样器:

@Component
public class LocalMetricsSampler {

    @Autowired
    private MeterRegistry meterRegistry;

    @Scheduled(fixedRate = 30000)
    public void sampleHttpPerformance() {
        Map<String, Object> data = queryMetricByTags("http.server.requests", 
                                                     Collections.singletonMap("uri", "/api/user"));

        Double total = extractValue(data, "TOTAL_TIME");
        Double count = extractValue(data, "COUNT");

        if (count > 0 && total / count > 0.1) {
            triggerAlert("High latency on /api/user: " + (total/count*1000) + " ms");
        }
    }

    private void triggerAlert(String msg) {
        log.warn("ALERT: {}", msg);
        // 发邮件、调 webhook...
    }
}

虽简单,但已具备基本闭环能力,适用于中小系统。


七、接入 Prometheus:打造真正的生产级监控体系

前面都是准备,这才是重头戏。

7.1 引入 micrometer-registry-prometheus

Maven:

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

Gradle:

implementation 'io.micrometer:micrometer-registry-prometheus'

引入后,Spring Boot 会自动注册 PrometheusMeterRegistry ,并通过条件装配启用 /actuator/prometheus 端点。

确保暴露它:

management:
  endpoints:
    web:
      exposure:
        include: prometheus

7.2 Prometheus 抓取配置

编辑 prometheus.yml

scrape_configs:
  - job_name: 'spring-boot-app'
    scrape_interval: 10s
    scrape_timeout: 5s
    static_configs:
      - targets: ['localhost:8080']
        labels:
          env: dev
          team: backend

重启 Prometheus,访问 UI → Status → Targets,看到 UP 状态才算成功。

测试查询:

http_server_requests_seconds_count

应该能看到数据。


7.3 性能调优与陷阱规避

  • 启用 gzip 压缩 :减少传输体积;
  • 限制标签基数 :防 cardinality explosion;
  • 聚合细粒度 URI :避免 /user/1 , /user/2 成千上万;
  • 使用 recording rules :预计算常用表达式,提升查询速度;
  • 慎用 Pushgateway :只适合短任务,不适合长期服务。

八、Grafana 可视化:让数据说话

最后一步,把数据变成看得懂的图表。

8.1 配置 Prometheus 数据源

Grafana → Configuration → Data Sources → Add → Prometheus
填入 URL,点击 Save & Test。

8.2 构建核心仪表板

JVM 内存趋势图
100 * jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}

单位:Percent,设置告警阈值 80%

HTTP QPS 与 P95 延迟

QPS:

rate(http_server_requests_seconds_count[1m])

P95:

histogram_quantile(0.95, sum by(le) (rate(http_server_requests_seconds_bucket[1m])))
Bar Gauge 显示线程数与 GC 次数
  • jvm_threads_live_threads
  • increase(jvm_gc_pause_seconds_count[5m])

8.3 告警闭环流程

flowchart TD
    A[Spring Boot App] --> B[Actuator]
    B --> C[Prometheus]
    C --> D[Grafana]
    D --> E[Alert]
    E --> F[通知 SRE]
    F --> G[修复]
    G --> A

形成完整可观测性闭环。


🎯 结语
这一套组合拳打下来,你的应用不再是“黑盒”,而是透明可控的系统。
从一行配置开始,到一整套监控体系落地,你已经掌握了现代 Java 工程师的核心技能之一。

现在,就去给你的服务加上 /actuator/metrics 吧!🚀

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:《Spring Boot Metrics:深入理解与应用》介绍了Spring Boot框架中的核心组件spring-boot-metrics,它通过集成Actuator实现对应用程序运行时指标的自动采集与监控。该工具可收集HTTP请求、JVM内存、线程状态、数据库查询等关键性能数据,并提供标准化端点如/actuator/metrics和/actuator/prometheus供外部系统读取。支持与Prometheus、Grafana等主流监控平台无缝集成,实现指标可视化。开发者还可通过@Timed、@Counted等注解及MeterRegistry接口自定义业务级度量指标,全面提升系统的可观测性与可维护性。本项目经过完整测试,适用于开发、测试与生产环境下的性能分析与故障排查。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值