跳转到内容
123xiao | 无名键客

《Spring Boot 中基于 Actuator + Micrometer + Prometheus 的应用监控实战与告警落地》

字数: 0 阅读时长: 1 分钟

Spring Boot 中基于 Actuator + Micrometer + Prometheus 的应用监控实战与告警落地

做 Spring Boot 服务时,很多团队一开始都把“监控”理解成两件事:服务活着日志能看。可一旦业务量上来,这两件事远远不够。

你会遇到这些很真实的问题:

  • 接口明明没报错,但 RT 一直升高
  • 某个实例 CPU 飙了,为什么?
  • 线程池积压了多久?
  • 数据库连接池是不是快耗尽了?
  • 某个版本上线后,错误率是不是在缓慢变高?
  • 服务还“活着”,但其实已经“不健康”了

这时候,应用指标监控就不是锦上添花,而是线上稳定性的基础设施。

这篇文章我会带你从一个可运行的 Spring Boot 示例出发,完成这样一条链路:

Spring Boot 应用 → Actuator 暴露指标 → Micrometer 统一采集 → Prometheus 抓取存储 → 告警规则落地

我会尽量按“带着你走一遍”的方式来写,重点不是背概念,而是把它真正跑起来,并知道常见坑怎么排。


背景与问题

在 Spring Boot 生态里,应用监控常见有三层:

  1. 健康检查:服务是否可用
  2. 指标采集:QPS、RT、错误率、JVM、线程池、连接池等
  3. 告警落地:指标异常时通知人

很多项目已经用了 spring-boot-starter-actuator,但仅停留在 /actuator/health。这只能回答“活没活”,却回答不了“跑得怎么样”。

而线上运维真正关心的是:

  • 请求量是否突增?
  • 延迟是否恶化?
  • 错误是否集中爆发?
  • 内存和 GC 是否异常?
  • 某个依赖是否拖慢整体链路?

所以这篇文章要解决的问题是:

如何在 Spring Boot 中,用 Actuator + Micrometer + Prometheus 搭建一套可观测、可告警、可排查的应用监控体系?


前置知识与环境准备

本文示例环境:

  • JDK 17
  • Spring Boot 3.x
  • Maven 3.8+
  • Prometheus 2.x

建议你具备这些基础:

  • 会写基本的 Spring Boot Web 接口
  • 知道 Maven 依赖怎么加
  • 对 Prometheus 的“拉取模式”有基础印象

本文示例目标

我们要实现以下内容:

  • 暴露 Actuator 端点
  • 输出 Prometheus 可识别的 metrics
  • 自定义业务指标
  • 接入线程池/接口耗时监控
  • 编写 Prometheus 抓取配置
  • 编写基础告警规则
  • 理解常见问题和生产注意事项

核心原理

先别急着写代码,先把这三个角色分清楚。

1. Actuator 是“暴露管理与观测端点”的入口

Spring Boot Actuator 提供一组端点,比如:

  • /actuator/health
  • /actuator/info
  • /actuator/metrics
  • /actuator/prometheus

它像是应用自带的“运维观察窗”。

2. Micrometer 是“指标抽象层”

Micrometer 可以理解为 Java 应用里的指标门面,它统一了这些指标概念:

  • Counter:累计计数
  • Gauge:瞬时值
  • Timer:耗时统计
  • DistributionSummary:分布统计

它不直接替代 Prometheus,而是屏蔽不同监控后端的差异。
你只需要针对 MeterRegistry 上报指标,后面接 Prometheus、InfluxDB 还是别的后端,代码风格都差不多。

3. Prometheus 是“抓取、存储、查询、告警”的后端

Prometheus 会定时拉取你的 /actuator/prometheus,把指标存下来。
然后通过 PromQL 做查询和告警判断。


一图看懂整体链路

flowchart LR
    A[Spring Boot 应用] --> B[Actuator 端点]
    B --> C[Micrometer 指标注册]
    C --> D[/actuator/prometheus]
    E[Prometheus] -->|定时抓取| D
    E --> F[告警规则 Alert Rules]
    F --> G[通知渠道]

指标从请求到告警的过程

sequenceDiagram
    participant U as 用户请求
    participant S as Spring Boot 服务
    participant M as Micrometer
    participant P as Prometheus
    participant A as Alert Rule

    U->>S: 调用接口 /order/create
    S->>M: 记录请求次数、耗时、状态码
    P->>S: 抓取 /actuator/prometheus
    S-->>P: 返回指标文本
    P->>A: 执行 PromQL 规则
    A-->>P: 满足阈值则触发告警

核心指标怎么理解

在实践中,我建议你先抓住 4 类指标,不要一上来铺太多:

1. 流量指标

例如:

  • 请求总数
  • 每秒请求数(QPS)
  • 按接口/状态码维度统计

2. 延迟指标

例如:

  • 平均响应时间
  • P95 / P99 延迟
  • 某个接口耗时异常

3. 错误指标

例如:

  • 5xx 次数
  • 错误率
  • 业务异常计数

4. 资源指标

例如:

  • JVM 内存
  • GC 次数与停顿时间
  • CPU
  • 线程池活跃线程数
  • 数据库连接池使用率

实战代码(可运行)

下面直接搭一个最小可运行示例。

第一步:创建项目并添加依赖

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>monitor-demo</artifactId>
    <version>1.0.0</version>
    <name>monitor-demo</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <!-- Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

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

        <!-- 可选:校验 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

第二步:配置 Actuator 与指标暴露

src/main/resources/application.yml

server:
  port: 8080

spring:
  application:
    name: monitor-demo

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always
  prometheus:
    metrics:
      export:
        enabled: true
  metrics:
    tags:
      application: ${spring.application.name}
    distribution:
      percentiles-histogram:
        http.server.requests: true
      percentiles:
        http.server.requests: 0.5,0.95,0.99

这里有几个点很关键:

  • exposure.include:决定哪些端点可以通过 HTTP 访问
  • prometheus enabled:开启 Prometheus 指标导出
  • management.metrics.tags.application:给所有指标统一打上应用名标签
  • percentiles-histogram:允许 Prometheus 统计接口延迟分位数

我自己在项目里经常漏掉最后这块,结果 PromQL 写 histogram_quantile 时发现数据不全,排查半天才发现直方图没开。


第三步:启动类

src/main/java/com/example/monitordemo/MonitorDemoApplication.java

package com.example.monitordemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MonitorDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(MonitorDemoApplication.class, args);
    }
}

第四步:写一个业务接口,并注入自定义指标

4.1 自定义指标服务

src/main/java/com/example/monitordemo/metrics/OrderMetricsService.java

package com.example.monitordemo.metrics;

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

import java.util.concurrent.TimeUnit;

@Service
public class OrderMetricsService {

    private final Counter successCounter;
    private final Counter failedCounter;
    private final Timer createOrderTimer;

    public OrderMetricsService(MeterRegistry meterRegistry) {
        this.successCounter = Counter.builder("biz_order_create_total")
                .description("订单创建成功总次数")
                .tag("biz", "order")
                .tag("result", "success")
                .register(meterRegistry);

        this.failedCounter = Counter.builder("biz_order_create_total")
                .description("订单创建失败总次数")
                .tag("biz", "order")
                .tag("result", "failed")
                .register(meterRegistry);

        this.createOrderTimer = Timer.builder("biz_order_create_duration")
                .description("订单创建耗时")
                .tag("biz", "order")
                .publishPercentileHistogram()
                .register(meterRegistry);
    }

    public void recordSuccess(long millis) {
        successCounter.increment();
        createOrderTimer.record(millis, TimeUnit.MILLISECONDS);
    }

    public void recordFailed(long millis) {
        failedCounter.increment();
        createOrderTimer.record(millis, TimeUnit.MILLISECONDS);
    }
}

4.2 模拟业务接口

src/main/java/com/example/monitordemo/controller/OrderController.java

package com.example.monitordemo.controller;

import com.example.monitordemo.metrics.OrderMetricsService;
import jakarta.validation.constraints.Min;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.ThreadLocalRandom;

@RestController
@Validated
public class OrderController {

    private final OrderMetricsService orderMetricsService;

    public OrderController(OrderMetricsService orderMetricsService) {
        this.orderMetricsService = orderMetricsService;
    }

    @GetMapping("/order/create")
    public String createOrder(@RequestParam(defaultValue = "100") @Min(1) int sleepMs,
                              @RequestParam(defaultValue = "false") boolean fail) throws InterruptedException {
        long start = System.currentTimeMillis();
        try {
            Thread.sleep(ThreadLocalRandom.current().nextInt(sleepMs, sleepMs + 50));
            if (fail) {
                throw new IllegalStateException("模拟订单创建失败");
            }
            long cost = System.currentTimeMillis() - start;
            orderMetricsService.recordSuccess(cost);
            return "ok";
        } catch (Exception e) {
            long cost = System.currentTimeMillis() - start;
            orderMetricsService.recordFailed(cost);
            throw e;
        }
    }

    @GetMapping("/ping")
    public String ping() {
        return "pong";
    }
}

第五步:给异常一个明确返回,方便观察状态码

src/main/java/com/example/monitordemo/controller/GlobalExceptionHandler.java

package com.example.monitordemo.controller;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Map<String, Object> handleException(Exception e) {
        return Map.of(
                "code", 500,
                "message", e.getMessage()
        );
    }
}

第六步:运行并验证指标

启动项目后,依次访问:

健康检查

curl http://localhost:8080/actuator/health

Prometheus 指标出口

curl http://localhost:8080/actuator/prometheus

模拟成功请求

curl "http://localhost:8080/order/create?sleepMs=120"

模拟失败请求

curl "http://localhost:8080/order/create?sleepMs=200&fail=true"

再看 Prometheus 出口:

curl http://localhost:8080/actuator/prometheus | grep biz_order

你会看到类似输出:

biz_order_create_total{application="monitor-demo",biz="order",result="success",} 3.0
biz_order_create_total{application="monitor-demo",biz="order",result="failed",} 1.0
biz_order_create_duration_seconds_count{application="monitor-demo",biz="order",} 4.0
biz_order_create_duration_seconds_sum{application="monitor-demo",biz="order",} 0.587

第七步:接入线程池指标

业务里线程池往往是问题高发区。
如果任务积压、活跃线程打满、队列堆积,接口慢是必然的。

下面演示如何注册线程池指标。

src/main/java/com/example/monitordemo/config/ThreadPoolConfig.java

package com.example.monitordemo.config;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
public class ThreadPoolConfig {

    @Bean(destroyMethod = "shutdown")
    public ThreadPoolExecutor orderExecutor(MeterRegistry meterRegistry) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                4,
                8,
                60,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100)
        );

        ExecutorServiceMetrics.monitor(meterRegistry, executor, "order_executor");
        return executor;
    }
}

然后写个接口模拟异步任务。

src/main/java/com/example/monitordemo/controller/AsyncController.java

package com.example.monitordemo.controller;

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

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;

@RestController
public class AsyncController {

    private final ThreadPoolExecutor orderExecutor;

    public AsyncController(ThreadPoolExecutor orderExecutor) {
        this.orderExecutor = orderExecutor;
    }

    @GetMapping("/async/task")
    public String submitTask() {
        orderExecutor.submit(() -> {
            try {
                Thread.sleep(ThreadLocalRandom.current().nextInt(500, 1500));
            } catch (InterruptedException ignored) {
                Thread.currentThread().interrupt();
            }
        });
        return "submitted";
    }
}

触发几次后,你可以在 /actuator/prometheus 中搜这些指标:

  • executor_active_threads
  • executor_pool_size_threads
  • executor_queued_tasks
  • executor_completed_tasks_total

第八步:配置 Prometheus 抓取

新建 prometheus.yml

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'spring-boot-demo'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080']
        labels:
          app: 'monitor-demo'

如果你本地已经有 Prometheus,可直接启动:

prometheus --config.file=prometheus.yml

启动后访问:

http://localhost:9090

在 Prometheus 中试几个查询。

查询 HTTP 请求总数

http_server_requests_seconds_count

查询最近 1 分钟订单成功速率

rate(biz_order_create_total{result="success"}[1m])

查询最近 5 分钟订单失败速率

rate(biz_order_create_total{result="failed"}[5m])

查询接口 P95 延迟

histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket[5m])) by (le, uri, method))

这里提醒一句:
uri 标签在高动态路径场景要特别小心,比如 /order/123456/order/987654 这种。如果路径模板没归一,标签基数会炸,Prometheus 很容易吃不消。


告警落地:从“有指标”到“能通知”

很多文章写到 Prometheus 查询就结束了,但真正落地监控,最关键的是告警。

先写一份基础规则。

alert-rules.yml

groups:
  - name: spring-boot-alerts
    rules:
      - alert: InstanceDown
        expr: up{job="spring-boot-demo"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "实例不可用"
          description: "Prometheus 连续 1 分钟抓取失败"

      - alert: HighHttp5xxErrorRate
        expr: |
          (
            sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
            /
            sum(rate(http_server_requests_seconds_count[5m]))
          ) > 0.05
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "HTTP 5xx 错误率过高"
          description: "最近 5 分钟 5xx 错误率超过 5%"

      - alert: HighApiLatencyP95
        expr: |
          histogram_quantile(
            0.95,
            sum(rate(http_server_requests_seconds_bucket[5m])) by (le, uri, method)
          ) > 1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "接口 P95 延迟过高"
          description: "最近 5 分钟 P95 延迟超过 1 秒"

      - alert: ThreadPoolQueueBacklog
        expr: executor_queued_tasks{executor="order_executor"} > 50
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "线程池队列积压"
          description: "order_executor 排队任务超过 50"

然后在 prometheus.yml 中引入:

rule_files:
  - alert-rules.yml

告警设计思路:不要只盯“阈值”,还要看“持续时间”

我建议你设计告警规则时遵守两个原则:

1. 阈值 + 持续时间

不要因为一个瞬时抖动就报警。
比如 P95 延迟偶发升到 1.2 秒,但只持续 10 秒,不一定值得半夜把人叫醒。

所以一般要加:

  • expr: 超过阈值
  • for: 持续多久再报警

2. 分级

最简单可以分成:

  • critical:实例挂了、核心接口不可用、错误率极高
  • warning:RT 高、线程池积压、连接池偏高

这样团队处理起来更有优先级。


监控对象分类图

classDiagram
    class 应用监控 {
      HTTP请求指标
      JVM指标
      线程池指标
      业务指标
      依赖资源指标
    }

    class HTTP请求指标 {
      QPS
      错误率
      P95/P99
    }

    class JVM指标 {
      Heap使用量
      GC次数
      线程数
    }

    class 线程池指标 {
      活跃线程
      队列长度
      完成任务数
    }

    class 业务指标 {
      下单成功数
      下单失败数
      支付超时数
    }

    应用监控 --> HTTP请求指标
    应用监控 --> JVM指标
    应用监控 --> 线程池指标
    应用监控 --> 业务指标

逐步验证清单

如果你是第一次搭这套链路,建议按下面顺序验证,不要一步到位。

应用侧

  • /actuator/health 可访问
  • /actuator/prometheus 可访问
  • 自定义指标出现在输出里
  • 接口访问后 http_server_requests_seconds_* 有数据
  • 线程池指标可见

Prometheus 侧

  • Status -> Targets 中目标状态为 UP
  • 能查询 up
  • 能查询 http_server_requests_seconds_count
  • 能查询你的自定义业务指标
  • 告警规则加载成功

告警侧

  • 故意制造 5xx
  • 故意制造高延迟
  • 观察告警是否按 for 时间触发
  • 恢复后观察告警是否自动消除

常见坑与排查

这部分很重要,我自己在项目里踩过不少。

1. /actuator/prometheus 访问不到

现象

返回 404 或 403。

排查

先看配置是否包含:

management:
  endpoints:
    web:
      exposure:
        include: prometheus

还要确认依赖是否加了:

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

如果用了 Spring Security,通常还要放行 Actuator 端点。


2. Prometheus 抓不到目标

现象

Prometheus Targets 页面显示 DOWN

排查思路

  • 应用是否真的启动在 8080
  • metrics_path 是否写对
  • 容器或服务器网络是否可达
  • 防火墙/安全组是否放行
  • 是否用了独立 management 端口却还在抓业务端口

如果配置了:

management:
  server:
    port: 8081

那 Prometheus 就应该抓 8081/actuator/prometheus,不是 8080


3. 指标有了,但 PromQL 查不到分位数

现象

histogram_quantile 没结果,或者结果不稳定。

原因

大概率是没有开启 Histogram。

检查:

management:
  metrics:
    distribution:
      percentiles-histogram:
        http.server.requests: true

对于自定义 Timer,也要确保调用了:

.publishPercentileHistogram()

4. 标签过多,Prometheus 内存上涨

现象

Prometheus 查询变慢,内存持续变大。

典型原因

高基数标签。

比如这些都要尽量避免:

  • 用户 ID
  • 订单 ID
  • 请求唯一流水号
  • 原始 URL 动态路径

错误示例:

Counter.builder("biz_request_total")
        .tag("userId", userId)
        .register(meterRegistry);

这是监控系统里非常危险的写法。
监控不是日志,不能把每个请求实例都塞成标签。


5. 告警太多,团队开始“免疫”

现象

告警群天天响,但真正的问题反而没人看。

建议

  • 先从少量高价值规则开始
  • 严格区分 warning / critical
  • 业务低峰期做规则压测
  • 给每条告警写明确处理手册
  • 避免“单实例偶发毛刺”级别的告警直接通知全员

我见过最典型的坑就是“任何 5xx 都报警”,结果一个短暂重试失败就开始刷屏。
最后大家都把群静音,真正故障来时也没人理。


6. 自定义指标重复注册

现象

启动时报 meter already exists,或者指标行为异常。

原因

同名指标 + 相同标签组合重复注册。

建议

  • 在单例 Bean 中初始化指标
  • 避免在请求方法里动态 register
  • 标签维度保持稳定

错误姿势通常像这样:

@GetMapping("/bad")
public String bad(MeterRegistry registry) {
    Counter.builder("test_counter").register(registry).increment();
    return "ok";
}

这会在每次请求时尝试注册,早晚出问题。


安全/性能最佳实践

监控系统本身也会带来成本,所以生产上要做约束。

1. 不要把所有 Actuator 端点都暴露到公网

推荐只暴露必要端点:

  • health
  • info
  • prometheus

并且尽量通过以下方式保护:

  • 内网访问
  • 网关鉴权
  • Spring Security
  • Service Mesh / 网络策略

示例:

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus

2. 生产环境优先使用独立 management 端口

这样做有几个好处:

  • 业务流量和管理流量隔离
  • 安全策略更容易控制
  • 抓指标时不影响业务端口策略

示例:

management:
  server:
    port: 8081

3. 控制标签基数

这个我愿意多强调一遍:标签设计决定 Prometheus 能不能稳定跑下去。

适合当标签的维度通常是:

  • 应用名
  • 实例名
  • 环境
  • 接口模板
  • 状态码
  • 业务类型枚举

不适合当标签的维度:

  • 用户 ID
  • 商品 ID
  • 订单号
  • Trace ID
  • 请求时间戳

4. 只采集“对行动有帮助”的指标

监控不是越多越好,而是要能支持决策。

比如这些指标通常非常值得保留:

  • HTTP 请求量、错误率、延迟
  • JVM 内存与 GC
  • 线程池队列长度与活跃线程
  • 数据库连接池活跃连接
  • 核心业务成功/失败次数

而一些“看起来很多,但没人会处理”的指标,可以先不采。


5. 抓取间隔要平衡实时性与成本

Prometheus 不是抓得越频繁越好。

常见建议:

  • 普通业务服务:15s30s
  • 非核心服务:30s60s
  • 超高频抓取要确认指标量和存储压力

如果你的服务实例很多,指标又多,5s 抓一次很快就会把后端压住。


6. 给关键业务补充自定义指标

默认 JVM 和 HTTP 指标只能告诉你“系统层面怎么了”,
但很多时候你真正想知道的是“业务层面怎么了”。

比如电商系统里更有价值的可能是:

  • 下单成功率
  • 支付失败数
  • 库存扣减冲突数
  • 优惠券核销失败数

这些指标才更贴近业务 SLA。


一套实用的监控落地建议

如果你准备把这套方案放进项目,我建议分三步推进:

第 1 阶段:先把基础指标接起来

至少要有:

  • health
  • prometheus
  • HTTP 请求指标
  • JVM 指标
  • 线程池指标

第 2 阶段:补齐业务指标

选择 2~3 个关键业务动作:

  • 成功数
  • 失败数
  • 耗时
  • 重试数

第 3 阶段:做告警收敛

优先上这些告警:

  • 实例不可用
  • 核心接口 5xx 错误率
  • 核心接口 P95 延迟
  • 线程池积压
  • 连接池耗尽风险

这样最容易形成闭环,而不是“监控搭了很多,看的人很少”。


总结

用一句话概括这套方案:

Actuator 负责暴露观测入口,Micrometer 负责统一指标模型,Prometheus 负责抓取、查询与告警。

落到 Spring Boot 实战里,你真正要做的事情其实不复杂:

  1. 引入 actuatormicrometer-registry-prometheus
  2. 暴露 /actuator/prometheus
  3. 利用 Micrometer 补充业务指标
  4. 用 Prometheus 抓取并写 PromQL
  5. 把高价值告警规则落地

如果你现在正准备在项目里上监控,我建议你先别追求“大而全”,先把下面 4 件事做稳:

  • 看得到 HTTP 请求量、错误率、P95
  • 看得到 JVM 和线程池
  • 有 2~3 个核心业务指标
  • 有少量但可靠的告警规则

当这四件事跑顺了,你的应用监控体系就已经不再是“摆设”,而是真能在故障来临前给你信号。

如果要给一个边界条件建议,那就是:

  • 小项目、低流量内部服务,不一定一开始就要上很复杂的监控大盘
  • 但只要服务开始承载业务责任,最基本的指标和告警一定要有
  • 监控建设的重点不是“采多少”,而是“出了问题能不能快速判断和行动”

这才是 Actuator + Micrometer + Prometheus 这套组合真正的价值。


分享到:

上一篇
《AI Agent 实战:基于大模型构建企业级多步骤任务自动化工作流》
下一篇
《AI 应用中的 RAG 实战:从向量检索、重排序到效果评估的完整落地指南》