Spring Boot Actuator
Spring Boot Actuator 是 Spring Boot 提供的生产可观测能力入口。它把应用内部的健康状态、指标、线程、环境配置、日志级别、自动配置条件等信息,通过标准端点暴露给开发、运维、监控系统和容器平台。
零基础先记住一句话:
Actuator 不是“多几个 URL”,而是让应用能被探活、被监控、被诊断、被平台治理。
如果没有 Actuator,线上系统经常只能靠“进程还在不在”“日志有没有报错”来猜状态。真实生产里,进程活着不代表服务可用:数据库可能断了,Redis 可能超时,线程池可能满了,下游接口可能不可用,自动配置可能没有生效。
学习目标
学完本章你要能回答:
- Actuator 解决什么问题,和普通业务接口有什么区别。
- Health、Liveness、Readiness 分别表示什么。
- K8s 为什么不能只用进程存活判断服务可用。
/metrics、/prometheus、Micrometer、Prometheus、Grafana 的关系。- 如何自定义
HealthIndicator、MeterBinder和业务指标。 /conditions、/env、/configprops、/threaddump怎么用于排查。- 为什么不能把所有 Actuator 端点暴露到公网。
- 医疗数据采集平台和订单系统中 Actuator 怎么落地。
为什么需要 Actuator
线上应用有三类问题,单靠日志很难快速判断。
| 问题 | 例子 | 没有 Actuator 会怎样 |
|---|---|---|
| 实例是否能接流量 | 应用启动了,但数据库连接池还没准备好 | 网关或 K8s 可能把流量打进异常实例 |
| 系统是否正在变慢 | 接口 P95 升高、线程池队列堆积 | 只能等用户反馈或翻大量日志 |
| 配置和自动配置是否正确 | 某个 Starter 没生效、Profile 错了 | 排查靠猜,难以定位条件不匹配 |
Actuator 的作用是把这些状态标准化地暴露出来:
flowchart TD
A["Spring Boot 应用"] --> B["Actuator 端点"]
B --> C["K8s 探针"]
B --> D["Prometheus 指标采集"]
B --> E["开发排查"]
B --> F["运维平台"]
C --> G["决定是否接流量或重启"]
D --> H["Grafana 看板和告警"]
E --> I["定位配置、线程、Bean、条件"]它不是替代日志,而是补齐日志不擅长的维度:
| 能力 | 日志 | Actuator / 指标 |
|---|---|---|
| 单次请求细节 | 强 | 弱 |
| 整体趋势 | 弱 | 强 |
| 实例健康 | 需要分析 | 标准端点直接返回 |
| 自动配置条件 | 日志不直观 | /conditions |
| 线程快照 | 需要进机器 | /threaddump |
| 日志级别调整 | 要改配置或重启 | /loggers 可动态调整 |
Actuator 整体链路
生产监控通常是这条链路:
flowchart TD
A["业务代码"] --> B["Micrometer 记录指标"]
B --> C["Actuator 暴露 metrics/prometheus"]
C --> D["Prometheus 定时拉取"]
D --> E["Prometheus 存储时间序列"]
E --> F["Grafana 展示看板"]
E --> G["Alertmanager 告警"]健康检查通常是这条链路:
flowchart TD
A["K8s 或网关"] --> B["访问 /actuator/health/readiness"]
B --> C{"应用是否准备好"}
C -- "是" --> D["继续转发流量"]
C -- "否" --> E["从流量池摘除"]
A --> F["访问 /actuator/health/liveness"]
F --> G{"进程是否仍可恢复"}
G -- "是" --> H["保持运行"]
G -- "否" --> I["重启容器"]这两个链路解决的问题不同:指标用于观察趋势和告警,健康检查用于平台控制流量和生命周期。
常见端点
| 端点 | 作用 | 生产建议 |
|---|---|---|
/actuator/health | 应用健康状态 | 可以暴露给探针,但细节要控制 |
/actuator/health/liveness | 存活状态 | 给 K8s livenessProbe |
/actuator/health/readiness | 就绪状态 | 给 K8s readinessProbe |
/actuator/metrics | 指标入口 | 内网或受控访问 |
/actuator/prometheus | Prometheus 格式指标 | 给 Prometheus 拉取 |
/actuator/info | 应用信息 | 可放版本、构建号,避免敏感信息 |
/actuator/env | Environment 配置源和值 | 敏感,必须鉴权 |
/actuator/configprops | 配置绑定结果 | 敏感,必须鉴权 |
/actuator/beans | Bean 列表 | 敏感,生产慎开 |
/actuator/conditions | 自动配置条件报告 | 排查自动配置很有用,生产慎开 |
/actuator/threaddump | 线程栈 | 排查卡顿,必须鉴权 |
/actuator/heapdump | 堆转储 | 极敏感,通常不对 Web 暴露 |
/actuator/loggers | 查看和调整日志级别 | 内网受控使用 |
最常见的生产暴露组合:
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus排查阶段可以临时开放 conditions、env、configprops、threaddump,但必须有权限控制,排查后及时关闭。
依赖和基础配置
Maven 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>基础配置:
management:
server:
port: 9090
endpoints:
web:
base-path: /actuator
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
probes:
enabled: true
show-details: never为什么建议管理端口和业务端口分离?
- 业务网关可以只转发 8080,不暴露 9090。
- 运维和监控系统走内网访问管理端口。
- 安全策略更清楚。
- 业务接口异常时,管理端口仍可能提供诊断能力。
验证:
curl http://localhost:9090/actuator/health
curl http://localhost:9090/actuator/prometheusHealth、Liveness、Readiness
很多人只知道 /health,但生产里更重要的是区分存活和就绪。
| 状态 | 含义 | 平台动作 |
|---|---|---|
| Liveness | 进程是否还活着,是否需要重启 | 失败时 K8s 重启容器 |
| Readiness | 应用是否准备好接收流量 | 失败时 K8s 摘除流量 |
| Health | 综合健康状态 | 给人或平台看整体状态 |
为什么不能混用?
假设应用启动了,但数据库暂时连接不上:
- Liveness 可以仍然是 UP,因为进程没有死,稍后可能恢复。
- Readiness 应该是 DOWN,因为此时不能接业务流量。
- 如果把数据库失败放进 Liveness,K8s 会不断重启容器,可能让问题更严重。
flowchart TD
A["应用进程启动"] --> B["Liveness UP"]
B --> C["初始化数据源、Redis、MQ"]
C --> D{"核心依赖可用吗"}
D -- "否" --> E["Readiness DOWN<br/>不接流量"]
D -- "是" --> F["Readiness UP<br/>接收流量"]Kubernetes 探针示例:
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 9090
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 2
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 9090
initialDelaySeconds: 60
periodSeconds: 20
timeoutSeconds: 2参数说明:
| 参数 | 说明 | 配错后果 |
|---|---|---|
initialDelaySeconds | 容器启动后多久开始探测 | 太短会在应用未启动完成时误杀 |
periodSeconds | 探测频率 | 太频繁会增加压力 |
timeoutSeconds | 探测超时 | 太短可能误判,太长会拖慢摘流 |
自定义 HealthIndicator
健康检查适合检查核心依赖是否可用,例如数据库、Redis、MQ、医院接口、采集通道。
医疗采集平台示例:检查医院接口是否能快速探活。
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class HospitalApiHealthIndicator implements HealthIndicator {
private final HospitalApiClient hospitalApiClient;
public HospitalApiHealthIndicator(HospitalApiClient hospitalApiClient) {
this.hospitalApiClient = hospitalApiClient;
}
@Override
public Health health() {
try {
boolean ok = hospitalApiClient.ping(1000);
if (ok) {
return Health.up()
.withDetail("hospitalApi", "reachable")
.build();
}
return Health.down()
.withDetail("hospitalApi", "unreachable")
.build();
} catch (Exception ex) {
return Health.down()
.withDetail("hospitalApi", "timeout")
.build();
}
}
}客户端 Demo:
import org.springframework.stereotype.Component;
@Component
public class HospitalApiClient {
public boolean ping(int timeoutMs) {
// Demo:真实项目这里调用医院网关的轻量探活接口,并设置短超时。
return timeoutMs > 0;
}
}健康检查原则:
| 原则 | 原因 |
|---|---|
| 必须轻量 | 探针会频繁访问,不能拖垮服务 |
| 必须有超时 | 下游卡死时不能把 health 也卡死 |
| 不查大表 | 健康检查不是业务报表 |
| 不泄露敏感信息 | 不能把账号、连接串、内部 IP 暴露出去 |
| 区分核心和非核心依赖 | 非核心依赖失败不一定要摘流 |
错误示例:
// 不推荐:健康检查里查询大量业务数据
select * from collect_result where collect_time > now() - interval 1 day;健康检查应该回答“依赖是否可达、服务是否可用”,不应该做大范围业务查询。
Micrometer 指标体系
Spring Boot Actuator 的指标底层通常由 Micrometer 统一抽象。Micrometer 可以把同一套指标输出给 Prometheus、Graphite、Datadog 等系统。
核心关系:
flowchart TD
A["业务代码"] --> B["MeterRegistry"]
B --> C["Counter / Timer / Gauge"]
C --> D["Actuator metrics"]
C --> E["Prometheus registry"]
E --> F["/actuator/prometheus"]常见指标类型:
| 类型 | 说明 | 示例 |
|---|---|---|
| Counter | 只增不减的计数 | 请求数、采集成功数、失败数 |
| Gauge | 可上下变化的瞬时值 | 队列长度、在线连接数 |
| Timer | 记录耗时和次数 | 接口耗时、下游调用耗时 |
| DistributionSummary | 记录分布 | 消息大小、批次大小 |
Counter Demo
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Component;
@Component
public class CollectMetrics {
private final Counter successCounter;
private final Counter failedCounter;
public CollectMetrics(MeterRegistry registry) {
this.successCounter = Counter.builder("collect_task_success_total")
.description("采集任务成功总数")
.tag("system", "hospital")
.register(registry);
this.failedCounter = Counter.builder("collect_task_failed_total")
.description("采集任务失败总数")
.tag("system", "hospital")
.register(registry);
}
public void success() {
successCounter.increment();
}
public void failed() {
failedCounter.increment();
}
}Timer Demo
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Component;
import java.time.Duration;
@Component
public class HospitalApiMetrics {
private final Timer requestTimer;
public HospitalApiMetrics(MeterRegistry registry) {
this.requestTimer = Timer.builder("hospital_api_request_duration")
.description("医院接口调用耗时")
.publishPercentiles(0.5, 0.95, 0.99)
.maximumExpectedValue(Duration.ofSeconds(10))
.register(registry);
}
public <T> T record(java.util.function.Supplier<T> supplier) {
return requestTimer.record(supplier);
}
}使用:
String result = hospitalApiMetrics.record(() -> hospitalApiClient.fetch("/patients"));Gauge Demo
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Component;
import java.util.concurrent.BlockingQueue;
@Component
public class QueueMetrics {
public QueueMetrics(MeterRegistry registry, BlockingQueue<Runnable> collectQueue) {
Gauge.builder("collect_queue_size", collectQueue, BlockingQueue::size)
.description("采集任务队列长度")
.register(registry);
}
}指标命名建议:
- 使用业务语义明确的名称。
- 计数器通常以
_total结尾。 - tag 不要放高基数字段,例如用户 ID、订单号、患者 ID。
- 指标要能驱动告警或排查,不要为了好看乱加。
为什么 tag 不能乱用?如果把 userId、orderNo、patientId 放进 tag,每个不同值都会生成一组时间序列,Prometheus 压力会急剧上升。
Prometheus 和 Grafana 怎么配合
Prometheus 拉取配置示例:
scrape_configs:
- job_name: "spring-boot-app"
metrics_path: "/actuator/prometheus"
static_configs:
- targets: ["app-1:9090", "app-2:9090"]Grafana 看板常见指标:
| 指标 | 说明 |
|---|---|
http_server_requests_seconds_count | HTTP 请求次数 |
http_server_requests_seconds_max | HTTP 最大耗时 |
jvm_memory_used_bytes | JVM 内存使用 |
jvm_gc_pause_seconds_count | GC 次数 |
hikaricp_connections_active | Hikari 活跃连接 |
system_cpu_usage | CPU 使用 |
| 自定义业务指标 | 采集成功率、订单失败数、队列长度 |
一个基础告警思路:
| 告警 | 判断 |
|---|---|
| 实例不可用 | readiness 连续失败 |
| 接口慢 | P95 超过阈值持续 5 分钟 |
| 错误率高 | 5xx 比例超过阈值 |
| 连接池紧张 | 活跃连接接近最大连接数 |
| 采集积压 | 队列长度持续增长 |
| 下游不稳定 | 医院接口 Timer P95 升高、失败 Counter 增长 |
排查自动配置:conditions
/actuator/conditions 是排查自动配置最重要的端点之一。
典型场景:你引入了一个自定义 Starter,但默认 Bean 没出来。
排查流程:
flowchart TD
A["默认 Bean 没创建"] --> B["访问 /actuator/conditions"]
B --> C["搜索目标 AutoConfiguration"]
C --> D{"是否出现在报告里"}
D -- "否" --> E["检查 starter 依赖和 imports/spring.factories"]
D -- "是" --> F{"Positive 还是 Negative"}
F -- "Negative" --> G["看缺少 class、property、bean 还是 web 条件"]
F -- "Positive" --> H["继续看是否 MissingBean 让位或 Bean 创建失败"]conditions 能回答:
- 自动配置类有没有被发现。
- 条件为什么匹配。
- 条件为什么不匹配。
- 是否被排除。
它不能直接回答所有问题。例如 Bean 构造方法异常、配置绑定失败,还需要看日志和 /configprops。
排查配置:env 和 configprops
/actuator/env
用于查看最终 Environment 中有哪些配置源和值。
适合排查:
- Profile 是否正确。
- 环境变量是否覆盖 yml。
- 命令行参数是否覆盖配置中心。
- 某个配置最终值是什么。
/actuator/configprops
用于查看 @ConfigurationProperties 的绑定结果。
适合排查:
- prefix 是否写错。
- 类型转换是否成功。
- 配置类是否注册。
- 默认值是否生效。
安全注意:这两个端点可能包含数据库地址、账号、密钥、Token。生产必须鉴权,并开启脱敏。
排查线程:threaddump
当接口卡死、CPU 高、请求积压时,可以看线程栈。
常见判断:
| 线程状态 | 可能含义 |
|---|---|
RUNNABLE 很多 | CPU 计算、忙循环、序列化、压缩、加密 |
WAITING 很多 | 等待队列、锁、连接、条件变量 |
BLOCKED | Java synchronized 锁竞争 |
| 大量 Tomcat 线程等待下游 | 下游慢、连接池满、HTTP 客户端超时长 |
| Hikari 获取连接等待 | 数据库连接池耗尽 |
排查链路:
flowchart TD
A["接口大量超时"] --> B["看指标 P95 和错误率"]
B --> C["看 threaddump"]
C --> D{"线程卡在哪里"}
D -- "数据库连接" --> E["查 Hikari 指标、慢 SQL、锁等待"]
D -- "HTTP 下游" --> F["查下游耗时、超时、重试"]
D -- "锁竞争" --> G["查 synchronized 或业务锁"]
D -- "CPU RUNNABLE" --> H["查热点代码和 GC"]动态调整日志级别
线上排查时,有时需要临时把某个包日志改成 DEBUG,但不想重启。
查看日志级别:
curl http://localhost:9090/actuator/loggers/com.example.collect修改日志级别:
curl -X POST http://localhost:9090/actuator/loggers/com.example.collect \
-H "Content-Type: application/json" \
-d "{\"configuredLevel\":\"DEBUG\"}"恢复:
curl -X POST http://localhost:9090/actuator/loggers/com.example.collect \
-H "Content-Type: application/json" \
-d "{\"configuredLevel\":\"INFO\"}"注意:
- 只能在受控内网使用。
- DEBUG 日志可能非常多,排查完要恢复。
- 不要把敏感数据打印出来。
商业场景一:医疗数据采集平台
医疗采集平台最常见的问题是:采集失败、医院接口慢、任务积压、下游不稳定、实例启动后还没准备好就接流量。
建议指标:
| 指标 | 含义 |
|---|---|
collect_task_success_total | 采集成功数 |
collect_task_failed_total | 采集失败数 |
collect_queue_size | 采集队列长度 |
hospital_api_request_duration | 医院接口耗时 |
collect_retry_total | 重试次数 |
collect_dead_letter_total | 死信或最终失败数量 |
建议健康检查:
| 依赖 | 是否影响 readiness | 原因 |
|---|---|---|
| 数据库 | 是 | 无法读取任务和写结果 |
| Redis | 看业务 | 如果强依赖缓存或锁,则影响 |
| MQ | 是 | 无法消费或投递采集任务 |
| 医院接口 | 看场景 | 单个医院失败不一定摘除整个服务 |
| 字典缓存 | 是 | 字典未加载可能导致解析错误 |
采集服务启动过程:
flowchart TD
A["应用启动"] --> B["Liveness UP"]
B --> C["加载配置和字典"]
C --> D["初始化数据源和 MQ"]
D --> E{"核心依赖是否就绪"}
E -- "否" --> F["Readiness DOWN"]
E -- "是" --> G["Readiness UP"]
G --> H["开始接收采集任务"]商业场景二:订单系统
订单系统关注的是:接口错误率、支付回调失败、库存扣减慢、数据库连接池、MQ 发送失败。
建议指标:
| 指标 | 含义 |
|---|---|
order_create_total | 创建订单次数 |
order_create_failed_total | 创建订单失败数 |
pay_callback_total | 支付回调次数 |
pay_callback_duplicate_total | 重复回调次数 |
inventory_deduct_duration | 库存扣减耗时 |
mq_send_failed_total | 消息发送失败 |
订单系统告警不能只看实例健康。实例 UP 但订单失败率升高,也必须告警。
安全边界
Actuator 最大的风险是暴露了太多内部信息。
高风险端点:
| 端点 | 风险 |
|---|---|
/env | 可能泄露配置、账号、Token |
/configprops | 可能泄露绑定后的敏感配置 |
/beans | 暴露内部 Bean 和类结构 |
/heapdump | 可能包含用户数据、Token、密码 |
/threaddump | 可能暴露业务类名、SQL、调用链 |
/loggers | 被滥用可能打开大量日志 |
生产建议:
management:
server:
port: 9090
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: never配合:
- 管理端口只允许内网访问。
- 网关不要转发 Actuator 端点到公网。
- 敏感端点必须鉴权。
- 开启配置脱敏。
- 排查临时开放端点后要关闭。
- heapdump 更推荐通过安全运维通道获取,不建议 Web 暴露。
生产排查流程
接口变慢
flowchart TD
A["接口变慢"] --> B["看 http 指标 P95/P99"]
B --> C["按 URI、状态码、实例分组"]
C --> D{"是否单实例异常"}
D -- "是" --> E["看该实例线程、GC、连接池"]
D -- "否" --> F["看数据库、Redis、MQ、下游接口"]
E --> G["threaddump / metrics"]
F --> H["下游指标和慢 SQL"]自动配置没生效
flowchart TD
A["Starter 默认 Bean 不存在"] --> B["看 /conditions"]
B --> C["看自动配置类是否出现"]
C --> D["看 Negative matches"]
D --> E["检查依赖、配置、Profile、用户 Bean"]
E --> F["看 /configprops 和日志"]连接池耗尽
看指标:
- Hikari active connections。
- Hikari pending threads。
- HTTP 请求耗时。
- 慢 SQL。
- 线程栈是否卡在获取连接。
处理方向:
| 原因 | 处理 |
|---|---|
| 慢 SQL 占连接 | 优化 SQL、索引、事务 |
| 事务过长 | 缩小事务边界 |
| 连接泄漏 | 开启泄漏检测,检查未关闭资源 |
| 并发过高 | 限流、队列、扩容 |
| 下游数据库慢 | 降级、读写分离、排查数据库 |
常见坑
| 坑 | 后果 | 正确做法 |
|---|---|---|
| 所有端点都暴露 | 敏感信息泄露 | 只暴露必要端点 |
| health 做重查询 | 探针拖垮服务 | 轻量检查并设置超时 |
| liveness 检查数据库 | 数据库短故障导致容器反复重启 | 数据库通常影响 readiness,不一定影响 liveness |
| readiness 永远 UP | 未准备好就接流量 | 依赖未就绪时返回 DOWN |
| 指标 tag 放用户 ID | 时间序列爆炸 | tag 只放低基数字段 |
| 只看 health 不看业务指标 | 业务失败但实例健康 | 补充错误率、耗时、队列等指标 |
| DEBUG 日志忘记恢复 | 日志量暴涨、磁盘压力 | 排查后恢复 INFO |
| prometheus 暴露公网 | 指标和内部路径泄露 | 内网访问和鉴权 |
面试标准回答
Actuator 是什么?
Actuator 是 Spring Boot 的生产可观测和运维端点体系,用来暴露健康检查、指标、环境、配置绑定、自动配置条件、线程栈、日志级别等信息。它让应用可以被 K8s 探活、被 Prometheus 采集、被开发排查,而不是只能靠进程和日志猜状态。
Liveness 和 Readiness 区别是什么?
Liveness 表示进程是否还活着,失败时平台通常重启容器;Readiness 表示应用是否准备好接流量,失败时平台应该摘除流量但不一定重启。数据库短暂不可用通常应该影响 Readiness,而不一定影响 Liveness,否则可能造成容器反复重启。
Actuator 怎么接入 Prometheus?
项目引入 spring-boot-starter-actuator 和 micrometer-registry-prometheus,开放 /actuator/prometheus 端点,Prometheus 按配置定时拉取指标并存储为时间序列,Grafana 再基于 Prometheus 数据展示看板和告警。
如何自定义业务指标?
使用 Micrometer 的 MeterRegistry 注册 Counter、Timer、Gauge 等指标。例如采集平台可以记录采集成功数、失败数、队列长度、医院接口耗时。指标 tag 要控制基数,不能把用户 ID、订单号、患者 ID 这类高基数字段放进去。
Actuator 有什么安全风险?/env、/configprops、/beans、/heapdump、/threaddump 等端点可能暴露配置、Token、类结构、内存数据和调用栈。生产环境应只暴露必要端点,管理端口走内网,敏感端点必须鉴权,健康检查细节不要对外暴露。
关联知识点
| 知识点 | 说明 |
|---|---|
| Spring Boot 从零到生产级掌握 | Spring Boot 主线 |
| Spring Boot 扩展点 | HealthIndicator、MeterBinder、FailureAnalyzer 所在扩展点体系 |
| 自动配置原理 | conditions 排查自动配置 |
| 配置体系 | env 和 configprops 排查配置 |
| 启动流程 | readiness、Runner、启动阶段 |
| Spring Boot 面试 | 标准回答与追问 |
| JVM 排查工具 | 线程、堆、GC 排查 |
| 生产监控与线上问题排查 | 日志、指标、Trace联动及CPU、OOM、线程池、连接池、容器故障Runbook |
| Spring Boot Admin | 将多个应用的Actuator端点集中展示和管理 |
本章小结
Actuator 的核心价值是让 Spring Boot 应用进入“可观测、可探活、可排查、可治理”的生产状态。健康检查决定实例能不能接流量,指标决定能不能提前发现趋势,conditions/env/configprops/threaddump 决定能不能快速定位问题。真正的生产级项目,不是接口能跑就结束,而是出问题时能被看见、能被定位、能被平台安全地处理。
