☰
Spring Boot Actuator 指标监控实战:端点、Micrometer 与 Prometheus 接入指南
2026/10/14 3:59:47 网站建设 项目流程

做后端时间长了,你会发现一个很朴素的事实:一个服务能不能长期稳定跑下去,很多时候不是看代码写得多漂亮,而是看线上出问题时你手里有没有足够的线索。Spring Boot Actuator 就是专门干这件事的——它是 Spring Boot 官方提供的生产级指标监控组件,能让应用把健康状态、运行指标、线程、堆内存、请求分布这些内部信息通过标准端点暴露出来。无论你是刚把第一个微服务部署上线的同学,还是已经在运维十几个实例的老手,这套指标监控体系都会是你排查线上问题时的第一手资料。这篇文章我会从端点体系、健康检查、Micrometer 指标整合、安全策略、配置实战、告警接入到踩坑实录,完整梳理一遍,每一段都是可以直接照着落地的内容。

1. Actuator 指标监控的核心价值:从日志追查到实时仪表盘

1.1 为什么最终选了 Actuator:对比裸日志排查的真实体验

其实很多人最初对监控的态度是“出问题我再慢慢查”。我之前参与过的一个模拟项目,负责人最初就是这种想法,结果某天夜里服务响应时间从 50 毫秒飙升到 2 秒,大家的第一反应是上服务器翻日志。日志确实有,问题在于日志量太大了,几十个实例同时在打,光定位到具体异常日志就花了一个多小时;更麻烦的是,线程池被占满之后,新请求根本进不来,应用日志本身都打不出来了——服务已经“饿死”了,可日志系统还在老老实实地把旧记录往外吐。

Actuator 的指标是完全不同的玩法。它直接告诉你线程池有多少活跃线程、连接池使用率是多少、JVM 堆当前占用多少、GC 暂停多久。同样是那次故障,如果当时有/actuator/metrics和/actuator/health,切到相关指标页面的那一瞬间就能看到 Tomcat 线程数已经顶到 200 上限,数据源连接数也耗尽,后面的排查完全不用靠猜。一个是案发后翻日志重构现场,一个是实时仪表盘,两种体验的差距比想象中大得多。

我总结下来,它解决的核心问题有四类:

  • 健康检查:服务还活着吗?依赖的数据库、缓存、消息队列还通不通?
  • 运行状态观察:内存、线程、GC、HTTP 请求量、堆栈、环境配置。
  • 现场问题定位:堆转储、线程转储、运行时动态改日志级别。
  • 外部监控对接:把指标暴露给 Prometheus 这类时序系统,做趋势告警。

这套能力不需要改业务代码,加依赖、做配置、选端点就能用,所以它才会成为 Spring Boot 应用接入可观测性的默认起点。

1.2 端点与指标的两层能力:一次把概念理清楚

先记住一个关键点:Actuator 有两层信息模型,一层是端点(Endpoints),一层是指标(Metrics)。很多人混在一起看,配置的时候特别容易乱。

端点可以理解成“即时回答问题的接口”。比如你问“服务现在健康吗”,health端点给你一个状态快照;你问“当前注册了哪些 Bean”,beans端点把清单列出来;你再问“数据库连接串密码是什么”,env端点甚至能把配置项翻出来。端点是离散的、当下的、静态或半静态的信息。

指标走的是另一条路。它是一个持续产生的时间序列数值,比如“过去 5 分钟 HTTP 请求成功率”“当前 JVM 堆已用内存”“订单接口的平均耗时 P95”。指标回答的问题是“这段时间发生了什么趋势”,它天然适合告警和聚合分析。Actuator 在指标这一层把 Micrometer 作为门面,默认向内存里登记一套 JVM、系统、HTTP、数据源等自动指标,同时可以推到不同的时序数据库去。

这两层能力的关系可以这样理解:端点负责“现场勘察”,指标负责“长期监测”。线上出了问题,先看指标趋势判断从什么时候开始恶化,再用端点去看现场细节。只有端点没有指标,你只能采样到当下状态;只有指标没有端点,趋势异常时你却少了顺藤摸瓜的抓手。我在实际工作中就是把两层同时开起来,比如health、prometheus常开,heapdump、threaddump按需手动开。

2. 关键端点逐一拆解:生产环境该看哪些,该挡哪些

2.1 端点全景:一张表搞清默认启用与暴露开关

Actuator 内置了一批端点,每个端点是独立的 ID,HTTP 访问路径默认是/actuator/{id}。注意一个容易懵的点:“启用”和“暴露”是两回事。端点先要启用(默认大多数启用),之后还要决定是否通过 Web 暴露出来。哪怕某个端点是启用状态,没在exposure.include里放行,HTTP 也访问不到。

我把核心端点整理成了一张速查表,实际配置时可以直接对着它做取舍。

端点作用默认启用默认 Web 暴露私有部署建议
health聚合健康状态是是开放
info应用自定义信息是是可开放
metrics查看可用指标名称是否按需开放
prometheus输出 Prometheus 格式指标是否内网开放
loggers查看并动态调整日志级别是否建议关闭或内网
threaddump输出线程快照是否按需开放
heapdump下载堆转储文件是否强烈建议关闭
env查看环境属性是否建议关闭
beans查看 Spring Bean 清单是否建议关闭
configprops查看配置属性是否建议关闭
mappings查看 URL 路由映射是否按需开放
scheduledtasks查看定时任务是否按需开放
shutdown优雅关闭应用否否保持关闭
httptrace查看最近 HTTP 请求追踪是否按需开放

这里面最值得重点关注的是health、prometheus、loggers和threaddump。health决定了探针和运维判断能不能依赖它;prometheus是外部监控系统的数据出口;loggers是动态排障神器但也容易被滥用;threaddump在面对“线程卡死在哪”这种问题时几乎是唯一快速手段。

2.2 健康检查与探针:让服务“自我体检”

health端点默认只返回一个聚合状态,它是把应用内部所有子健康检查打包汇总的结果。数据源连接、Redis、消息队列这些组件在自动装配时都会提供自己的HealthIndicator。比如DataSourceHealthIndicator会执行一个校验查询来确认数据库连接可用,RedisHealthIndicator会发一个 ping。聚合逻辑是:只要有一个关键项 DOWN,整体状态就是 DOWN;全部 UP 才返回 UP。

生产环境有个细节很关键:show-details默认是never,外部探针只能看到 UP/DOWN,看不到具体是哪个依赖挂了。这对 Kubernetes 探针其实够了,但你在本地调试想看到细节,可以加management.endpoint.health.show-details=always。线上更推荐when_authorized,配合安全认证让细节只对管理员可见。我的习惯是本地看细节用always,线上坚持never,避免给攻击者信息投喂。

自定义健康检查也很简单,实现一个HealthIndicator接口注册成 Bean 即可:

@Component public class OrderServiceHealthIndicator implements HealthIndicator { @Override public Health health() { // 模拟检查下游服务连通性 boolean reachable = checkOrderService(); if (reachable) { return Health.up() .withDetail("orderService", "available") .build(); } return Health.down() .withDetail("orderService", "unreachable") .build(); } }

这个接口适合放到依赖了外部非标准组件的场景,比如快速验证某个私有协议的服务是否可达,如果系统里只有标准数据源和中间件,默认的健康检查已经够用。

从 Spring Boot 2.3 开始还多了一类探针端点。开启management.endpoint.health.probes.enabled=true之后,会额外暴露/actuator/health/liveness和/actuator/health/readiness。这两个探针对应容器编排系统的两种生命周期判断:存活探针决定要不要重启实例,就绪探针决定流量能不能打进来。这里有一个必须记住的原则:不要把就绪失败和存活失败混为一谈。数据库连接池暂时耗尽的时候,就绪应该返回失败,让上游把流量摘走,而不是让编排系统立刻重启容器;否则短时间内大规模重启,雪上加霜。

2.3 指标度量体系:Micrometer 是真正的功臣

很多人以为 Actuator 自带完整指标,其实指标底层是 Micrometer 在干活。Actuator 负责暴露端点,Micrometer 负责收集和注册指标,两者都引入之后才能把/actuator/metrics、/actuator/prometheus用起来。

Micrometer 里的指标类型我简单归纳成四类:

指标类型数据特点典型场景
Counter只增不减累计请求数、累计订单数
Gauge可升可降当前线程数、当前堆内存使用量
Timer记录耗时分布接口耗时、数据库查询耗时
DistributionSummary记录分布且可自定义订单金额分布、消息体积分布

选型上有两个常见误区。一是把 Counter 当成 Gauge 用,比如想记录“当前在线用户数”,你如果用一个只增不减的 Counter,它根本不会掉下来;正确做法是用 Gauge。二是只记录平均耗时,忽略了 P99。平均值容易掩盖极端情况,200 个请求里 199 个 10 毫秒完成,一个 3 秒超时,平均值看起来还行,但真实用户已经有 0.5% 的请求不可用了。所以 Timer 我一般会配publishPercentiles(0.95, 0.99),让系统直接暴露分位耗时。

开箱即用的指标其实已经很全了。启动 JVM 应用之后,/actuator/metrics/能看到jvm.memory.used、jvm.gc.pause、system.cpu.usage、http.server.requests、tomcat.threads.busy这些。也就是说你什么都不用写,只要把端点暴露出来,基础可观测性就有了。真正的难点在于业务指标埋点,因为框架不可能知道你们“下单成功数”是什么含义,这部分要靠自己定义,后面会专门讲。

3. 从零到一的接入实战:依赖、安全、埋点、告警都配齐

3.1 引入依赖和暴露配置:第一天就能看到指标

先加依赖。如果用的是 Maven,在pom.xml里加:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>

第一个依赖是 Actuator 本体,第二个依赖是为了输出 Prometheus 格式的指标。如果你当前不需要 Prometheus,只加第一个也能看/actuator/metrics;但我的建议是一步到位加上,因为后面接入告警和趋势图几乎必然要用。注意如果项目是 Spring Boot 1.x,配置方式和端点路径差异很大,现在 1.x 已经非常老了,能升级尽量升级;下面所有配置都以 2.x/3.x 为准。

然后改application.yml:

management: endpoints: web: exposure: include: health,info,prometheus endpoint: health: show-details: when_authorized probes: enabled: true

不加任何配置时,默认只暴露health。上面这段把health、info、prometheus放行出去了。启动项目后直接访问:

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

看到 JSON 和文本格式的指标就说明第一步通了。info端点默认是空对象,需要你手动补充信息,比如:

info: app: name: order-service version: 1.0.0

这样访问/actuator/info就能看到应用标识了。还有一个常见需求是把监控服务独立端口,避免和业务接口混在一起,后面安全配置里再展开。

3.2 安全边界:线上暴露出问题之前先堵住

Actuator 把信息暴露得太方便了,所以安全问题必须前置。先说两个容易被忽略的坑:env端点可以把你数据库密码、Redis 连接串、各种密钥全部带出来;heapdump端点下载的堆转储文件在 JVM 内存充足时能到几个 GB,里面可能包含用户身份证、手机号等敏感数据。所以这两个端点在生产环境宁可不用,也不要打开公网访问。

我常用的生产方案是两条路并行。第一,把监控端口和业务端口分开:

management: server: port: 9091 address: 127.0.0.1

这样业务请求走 8080,监控信息走 9091。然后运维层面不让外部访问 9091,只有内网监控系统或授权跳板机可以连。第二,如果必须走同一个端口,就接 Spring Security 做认证,给监控端点和白名单路由写明确放行规则:

@Configuration public class ActuatorSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth -> auth .requestMatchers("/actuator/health", "/actuator/prometheus").permitAll() .anyRequest().authenticated()); return http.build(); } }

这样健康检查和指标采集放行给探针和监控系统,其余 Actuator 端点全部要求登录。实际项目里我会更激进一点:把不需要对外暴露的端点全部关掉,包括env、beans、configprops、heapdump、threaddump。这些端点确实好用,但它们不是常态监控必需品,真到排查现场时临时开启再关闭成本也不高。

3.3 业务埋点与自定义端点:让你的业务可观测

框架自带的指标是基础设施,业务指标得自己埋。最常见的是下单场景:我想知道“累计创建了多少订单”“下单接口的 P95 耗时是多少”。方法是在服务里注入MeterRegistry,注册一个Counter和一个Timer:

@Service public class OrderService { private final Counter orderCreateCounter; private final Timer orderCreateTimer; public OrderService(MeterRegistry registry) { this.orderCreateCounter = Counter.builder("order.create.total") .description("累计创建订单数") .tag("channel", "app") .register(registry); this.orderCreateTimer = Timer.builder("order.create.duration") .description("创建订单耗时") .publishPercentiles(0.5, 0.95, 0.99) .register(registry); } public Order createOrder(OrderRequest request) { long start = System.nanoTime(); try { // 实际业务逻辑 return doCreate(request); } finally { orderCreateTimer.record(() -> System.nanoTime() - start, TimeUnit.NANOSECONDS); orderCreateCounter.increment(); } } }

埋点之后,/actuator/prometheus里就会出现order_create_total和order_create_duration_seconds这样的指标。计数器名称我用小写下划线风格,这是 Micrometer 和 Prometheus 都推荐的标准命名。

如果还有比较特殊的查询需求,可以自定义端点。例如想做一个“订单统计”的专用接口:

@Endpoint(id = "orderStats") @Component public class OrderStatsEndpoint { @ReadOperation public Map<String, Object> stats() { return Map.of( "totalOrders", 128000L, "todayOrders", 360L ); } }

然后在exposure.include里加上orderStats,访问/actuator/orderStats就能拿到 JSON 数据。这个机制适合那些“不想为一次内部查询专门写一个 Controller”的场景,但要注意自定义端点同样要遵守安全暴露规范。

3.4 对接 Prometheus 与 Grafana:趋势、面板、告警一起办

本地看端点只是一部分,监控闭环还得靠外部系统。最常用的组合是 Prometheus 采集时序指标,Grafana 画面板和告警。Prometheus 配置一个抓取任务:

scrape_configs: - job_name: 'order-service' metrics_path: '/actuator/prometheus' static_configs: - targets: ['127.0.0.1:9091']

抓取路径就是要对应刚才的暴露端点和端口。如果你用了management.server.port=9091,这里的 targets 就写 9091,否则默认是业务端口。抓取间隔我习惯保持默认 15 秒,对 Actuator 开销可以忽略;不要为了“实时”调到 1 秒,有些场景下指标暴露本身的开销反而会超过业务代码。

Grafana 里导入一个 Spring Boot 相关面板模板,马上能看到 JVM 堆曲线、GC 次数、HTTP 流量、线程池状态这些核心图。我的建议是面板不需要太花哨,核心看四块:JVM 堆内存趋势、GC 暂停次数、HTTP 请求 P95/P99、数据库连接池活跃连接数。这四块能覆盖绝大多数线上故障场景。

告警规则用 Prometheus 原生写法即可。比如“HTTP 5xx 比例超过 5% 并持续 5 分钟”:

groups: - name: order-alerts rules: - alert: HighErrorRate expr: | sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_count[5m])) > 0.05 for: 5m labels: severity: warning

这套规则延后的是“基于指标趋势告警”,而不是单条日志报错就轰炸你。告警阈值不要照抄网上的通用值,不同业务的正常错误率相差很大,我会在接入后观察两周基线再定阈值。

4. 常见问题排查与线上经验速查

4.1 端点一直 404?按这四个顺序查

遇到过很多新手给我发“/actuator/metrics访问不了”的消息,截图里清一色 404。这类问题其实有固定的排查顺序:

第一,确认监控端点是否暴露。exposure.include没写就是默认只暴露health。写多个端点时用逗号分隔,例如include: health,metrics,prometheus。如果图省事全部放行,写include: "*",注意 YAML 里星号要加引号,否则会被解析成布尔或语法错误。

第二,路径前缀对不对。默认前缀是/actuator,但如果你改了management.endpoints.web.base-path,路径就变了。我见过有人把base-path改成/monitor,却还在访问/actuator/health,自然是 404。

第三,端点是不是没有启用。大多数端点默认启用,但shutdown之类默认关闭;部分版本和扩展端点也可能默认禁用。需要显式启用时加management.endpoint.xxx.enabled=true。

第四,HTTP 端点和 JMX 端点是独立开关。JMX 方式能看到某个端点,不代表 Web 方式能访问;反之亦然。如果你在用可视化工具或测试 JMX,要看对入口。

4.2 Security 加入后健康检查崩了:认证放行与细节隐藏

一个很常见的坑:给项目引入 Spring Security 之后,Kubernetes 就绪探针访问/actuator/health开始返回 401,容器被无限重启。原因很简单——默认的SecurityFilterChain把所有请求都拦住了,包括健康检查端点。

解决方式就是在安全过滤链里把健康检查端点放行,前面代码片段已经给了方案。不过还有一个隐含坑:show-details: when_authorized需要当前用户通过认证且有一定权限,细节信息才会返回。如果探针只是看 UP/DOWN,不受影响;但如果你在浏览器里登录后想看到具体是哪个依赖 DOWN 了,却只有 UP/DOWN,大概率就是权限配置还没到位。

另外要注意,在 Spring Security 配置里,2.6 之前和之后的路由匹配语法不太一样。如果你使用的版本比较老,requestMatchers可能不适用,要换回antMatchers。我遇到过配置复制过去却报错的情况,基本都是版本差异问题。

4.3 生产环境别乱动的端点:heapdump、threaddump、loggers 的边界

这三个端点虽然好用,但生产环境要谨慎。heapdump会触发一次 Full GC 之后再导出堆文件,JVM 堆有好几个 GB 时,导出过程中业务会影响很严重,我在一个模拟压力测试中实际看到过接口耗时翻倍。线上排查堆泄漏是必要的,但选时段比操作本身更重要,凌晨低峰期操作,别在流量高峰做。

threaddump影响相对小,但线程数多的情况下导出的文件也有几 MB,而且诊断线程阻塞时,抓取那一刻的 dump 才有意义。我的建议是:连续抓 3 次,间隔 5 秒,对比线程堆栈变化,比抓一次更能定位死锁或卡住的位置。

loggers端点的风险在于容易被滥用。它能动态把任意类的日志级别调到 DEBUG,这在排障时确实救命;但如果暴露到公网,等于把日志开关交给所有人。某次安全扫描就扫描出了线上环境的loggers端点对外开放,这个教训让我把所有动态控制类端点全部收进内网。

4.4 关于阈值、抓取频率和度量基线的心得

这个章节放在最后,其实是我最想强调的东西。指标监控不是配好了就一劳永逸,阈值必须跟着业务演进走。我见过一个团队把“请求数突增”告警阈值定得很低,结果每次促销活动都疯狂报警,到最后大家直接屏蔽告警,真正的故障发生在被屏蔽的噪声里。阈值方案要定期复查,尤其是大版本发布后。

还有抓取频率的问题,Prometheus 默认 15 秒抓一次,对于绝大多数 Spring Boot 服务是合适的。不要为了让面板看起来“实时”就调到 3 秒、1 秒,指标不是性能监控里的 CPU 采样,它的目标是一段时间内的趋势,不是毫秒级现场。抓取过密在实例上会产生额外的线程和内存消耗,指标一多,反而干扰判断。

最后留一个建议:每次发版后,拿/actuator/prometheus里的指标和上一版本做一次对比,重点看 JVM 内存曲线和请求耗时分布。很多性能回退在灰度期就会露出端倪,等用户开始反馈再去查已经晚了。这个习惯救过我太多次了。

还有一个小技巧:如果团队还没有统一的监控平台,可以先用 Spring Boot Admin 快速搭一个可视化界面,它能直接读取 Actuator 的health、metrics、loggers、threaddump,不用自己写页面。等接入 Prometheus 和 Grafana 之后,再用 Admin 做实例管理和日志级别动态调整。两条线不冲突,反而是互补的——这个问题我在多个项目里实践过,效果都不错。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询