Spring Boot Actuator 生产监控实践
提出问题
微服务上线后,运维团队最关心的事:服务还活着吗?健康检查能不能过?内存是不是快爆了?接口响应变慢了没有? 没有统一监控入口,每排查一个问题就要登机器、看日志、查指标,效率极低。Spring Boot Actuator 是 Spring 生态内置的「生产就绪」模块,直接暴露 HTTP 端点暴露健康、指标、环境、日志等运行时信息,是微服务监控的标配入口。
面试官问 Actuator,通常不是考你配了几个端点,而是看能不能真正用于生产——端点安全怎么控制?指标怎么接 Prometheus?优雅停机怎么做? 下面从实现原理、生产配置、踩坑案例三个维度展开。
分析问题
Actuator 端点体系:从注册到暴露
Actuator 的端点体系基于 Endpoint 接口抽象,每个端点实现 InvocationTarget 并通过 EndpointDiscoverer 自动注册。Spring Boot 启动时,WebEndpointServletHandlerMapping 将端点映射到 @RequestMapping 的 HTTP 路径上。
这个过程不是简单的 Bean 注入:每个端点有 enabled 和 exposed 两层开关。management.endpoints.enabled-by-default 控制是否启用,management.endpoints.web.exposure.include 控制是否对外暴露。启用但不暴露 = 可用但不开放,这个设计是为了安全——内部可以通过 JMX 调用,但不暴露 HTTP 端口。
核心端点与自定义
生产环境常用的端点:
| 端点 | 路径 | 作用 |
|---|---|---|
| health | /actuator/health | 健康检查,K8s liveness/readiness probe |
| info | /actuator/info | 应用信息(版本、构建时间) |
| metrics | /actuator/metrics | JVM 内存、线程、GC 等指标 |
| env | /actuator/env | 环境变量与配置属性 |
| loggers | /actuator/loggers | 运行时动态修改日志级别 |
| prometheus | /actuator/prometheus | 暴露 Prometheus 格式指标 |
自定义 HealthIndicator 是高频需求。 比如数据库连接、Redis 连通性、下游服务健康检查:
@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 端点。
配置只需要:
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.seconds | max > 1s | CMS 或 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,内存消耗爆炸。解决:用 @Timed 或 WebMvcMetricsFilter 的 ignoreTags 过滤掉动态部分,或者对 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 |
生产推荐:内网绑定 + 最小暴露:
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 内的活跃请求完成(通过 DispatcherServlet 的 inFlightRequestCount 计数),最后关闭容器。
配合 Actuator 的 health 端点,在 K8s preStop hook 中先将实例标记为 DOWN,让流量切走再关闭:
server.shutdown: graceful
spring.lifecycle.timeout-per-shutdown-phase: 30sK8s preStop:
lifecycle:
preStop:
exec:
command: ["curl", "-X", "POST", "http://localhost:8081/actuator/shutdown"]踩坑 3:优雅停机 + 长连接 WebSocket 的残留。如果服务有 WebSocket 长连接,server.shutdown=graceful 只对 HTTP 请求生效,WebSocket 连接不会被关闭。需要单独实现 WebSocketHandler 的 onClose 回调,并在 preStop 中发送关闭帧。没有处理的话,流量切走后长连接还挂着,客户端会收到 502。
动态日志级别的生产用法
/actuator/loggers 端点允许在运行时修改 Logger 的日志级别,无需重启。这是一个超实用的线上排查手段:
# 查看某个 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 官方文档