Skip to content

Spring Boot Actuator 生产监控实践

提出问题

微服务上线后,运维团队最关心的事:服务还活着吗?健康检查能不能过?内存是不是快爆了?接口响应变慢了没有? 没有统一监控入口,每排查一个问题就要登机器、看日志、查指标,效率极低。Spring Boot Actuator 是 Spring 生态内置的「生产就绪」模块,直接暴露 HTTP 端点暴露健康、指标、环境、日志等运行时信息,是微服务监控的标配入口。

面试官问 Actuator,通常不是考你配了几个端点,而是看能不能真正用于生产——端点安全怎么控制?指标怎么接 Prometheus?优雅停机怎么做? 下面从实现原理、生产配置、踩坑案例三个维度展开。

分析问题

Actuator 端点体系:从注册到暴露

Actuator 的端点体系基于 Endpoint 接口抽象,每个端点实现 InvocationTarget 并通过 EndpointDiscoverer 自动注册。Spring Boot 启动时,WebEndpointServletHandlerMapping 将端点映射到 @RequestMapping 的 HTTP 路径上。

这个过程不是简单的 Bean 注入:每个端点有 enabledexposed 两层开关。management.endpoints.enabled-by-default 控制是否启用,management.endpoints.web.exposure.include 控制是否对外暴露。启用但不暴露 = 可用但不开放,这个设计是为了安全——内部可以通过 JMX 调用,但不暴露 HTTP 端口。

核心端点与自定义

生产环境常用的端点:

端点路径作用
health/actuator/health健康检查,K8s liveness/readiness probe
info/actuator/info应用信息(版本、构建时间)
metrics/actuator/metricsJVM 内存、线程、GC 等指标
env/actuator/env环境变量与配置属性
loggers/actuator/loggers运行时动态修改日志级别
prometheus/actuator/prometheus暴露 Prometheus 格式指标

自定义 HealthIndicator 是高频需求。 比如数据库连接、Redis 连通性、下游服务健康检查:

java
@Component
public class DownstreamServiceHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        try {
            ResponseEntity<String> resp = restTemplate.getForEntity(
                "https://downstream/api/health", String.class);
            if (resp.getStatusCode().is2xxSuccessful()) {
                return Health.up().withDetail("status", "ok").build();
            }
        } catch (Exception e) {
            return Health.down().withDetail("error", e.getMessage()).build();
        }
        return Health.unknown().build();
    }
}

这样 K8s 的 liveness probe 就能感知到下游不可用,自动触发 Pod 重启,避免全链路雪崩。

踩坑 1:HealthIndicator 聚合导致 cascading down。Actuator 默认把所有 HealthIndicator 的结果聚合到 /actuator/health,任何一个下游 DOWN 都会导致整体 DOWN。这在健康检查场景下是对的——如果数据库断了,服务确实不可用。但有一种情况需要注意:缓存组件不可用,业务应该降级而非挂掉。解决方案:对 Redis、Cache 等非关键组件,用 HealthIndicator 返回 Status.UNKNOWN 而非 DOWN,这样聚合结果不会变成 DOWN,但监控端仍然能感知到异常。

Micrometer + Prometheus 指标对接

Spring Boot 2.x+ 内置 Micrometer 作为指标门面,默认对接 Prometheus。Micrometer 的实现原理是:每个 MeterRegistry 持有 Meter 的注册表,Meter 是带标签的度量值(Counter、Gauge、Timer、DistributionSummary)。Prometheus 注册表通过 PrometheusMeterRegistry 将 Meter 数据序列化为 Prometheus 文本格式的 /actuator/prometheus 端点。

配置只需要:

yaml
management.endpoints.web.exposure.include: health,info,metrics,prometheus
management.metrics.export.prometheus.enabled: true
management.metrics.tags.application: ${spring.application.name}

Micrometer 自动采集的指标涵盖:JVM 内存(jvm.memory.used)、GC 次数(jvm.gc.pause)、线程池状态(jvm.threads.live)、Tomcat 请求(tomcat.requests.total)、数据库连接池(hikaricp.connections.active)。

生产经验:至少把以下指标接入告警:

指标告警阈值常见原因
jvm.memory.used / 堆上限> 80%内存泄漏、未回收的 ThreadLocal、大对象
jvm.gc.pause.secondsmax > 1sCMS 或 G1 的 Full GC、年老代碎片
hikaricp.connections.active / max> 85%连接泄漏(未 close)、并发激增
http.server.requests.seconds P99> 200ms慢 SQL、外部调用超时、CPU 瓶颈

踩坑 2:Micrometer 的 tags 高基数爆炸http.server.requests 默认按 URI + 状态码 + 方法 + 异常 做标签组合。如果 URI 包含动态参数(如 /user/12345/order/20260721-001),Prometheus 会为每个不同 URI 创建独立的 time series,内存消耗爆炸。解决:用 @TimedWebMvcMetricsFilterignoreTags 过滤掉动态部分,或者对 URI 做归一化(如将 /user/{id} 作为模板)。

端点安全与优雅停机

直接暴露 /actuator/env/actuator/loggers 到公网是安全大忌——env 暴露数据库密码、API Key 等配置明文;loggers 可被恶意改成 DEBUG 把日志刷爆磁盘。

安全策略对比

方案实现方式优点缺点
内网绑定management.server.port=8081; address=127.0.0.1零依赖,最简单需配合 K8s Service 或 Nginx 内网隔离
Spring Security + Role.requestMatchers("/actuator/**").hasRole("ADMIN")精细化控制多一层依赖,配置复杂
只暴露读端点仅暴露 health,info,prometheus最小攻击面写操作(loggers、shutdown)只能 JMX
混合方案内网绑 + 暴露读端点 + JMX 写操作综合最优运维需额外配置 JMX

生产推荐:内网绑定 + 最小暴露:

yaml
management.server.port: 8081
management.server.address: 127.0.0.1
management.endpoints.web.exposure.include: health,info,prometheus

优雅停机:Spring Boot 2.3+ 支持 server.shutdown=graceful,底层原理:GracefulShutdown 类注册一个 SmartLifecycle,在 ApplicationContext close 时,先关闭 Tomcat 的 Connector(停止接受新请求),然后等待 graceful-shutdown-timeout 内的活跃请求完成(通过 DispatcherServletinFlightRequestCount 计数),最后关闭容器。

配合 Actuator 的 health 端点,在 K8s preStop hook 中先将实例标记为 DOWN,让流量切走再关闭:

yaml
server.shutdown: graceful
spring.lifecycle.timeout-per-shutdown-phase: 30s

K8s preStop:

yaml
lifecycle:
  preStop:
    exec:
      command: ["curl", "-X", "POST", "http://localhost:8081/actuator/shutdown"]

踩坑 3:优雅停机 + 长连接 WebSocket 的残留。如果服务有 WebSocket 长连接,server.shutdown=graceful 只对 HTTP 请求生效,WebSocket 连接不会被关闭。需要单独实现 WebSocketHandleronClose 回调,并在 preStop 中发送关闭帧。没有处理的话,流量切走后长连接还挂着,客户端会收到 502。

动态日志级别的生产用法

/actuator/loggers 端点允许在运行时修改 Logger 的日志级别,无需重启。这是一个超实用的线上排查手段:

bash
# 查看某个 Logger 的当前级别
curl http://localhost:8081/actuator/loggers/com.example.service.OrderService

# 临时将某个包的日志改为 DEBUG
curl -X POST -H "Content-Type: application/json" \
  -d '{"configuredLevel": "DEBUG"}' \
  http://localhost:8081/actuator/loggers/com.example.service

# 恢复默认
curl -X POST -H "Content-Type: application/json" \
  -d '{"configuredLevel": null}' \
  http://localhost:8081/actuator/loggers/com.example.service

注意:这个端点只对运维通道开放(内网绑 + RBAC 鉴权),且改完后记得恢复,否则 DEBUG 日志在线上跑一天能把磁盘撑爆。

总结

  • Actuator 的端点体系基于 Endpoint 接口 + WebEndpointServletHandlerMapping 自动注册,有两层开关(enabled + exposed)
  • 自定义 HealthIndicator 是生产必备,注意非关键组件用 UNKNOWN 避免 cascading down
  • Micrometer + Prometheus 是最佳指标组合,核心告警围绕 JVM 内存、GC、连接池、接口延迟建立;注意 tags 高基数问题
  • 优雅停机靠 server.shutdown=graceful + preStop hook + 请求计数,WebSocket 长连接需要额外处理
  • 安全策略组合:内网绑(8081)+ 最小暴露 + JMX 写操作,是生产环境最平衡的方案

参考

参考:Spring Boot Actuator 官方文档;Micrometer 1.12 参考指南;Spring Boot 2.3 优雅停机提案;K8s Pod Lifecycle 官方文档

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。