资讯动态

Spring Boot健康检查实战:Actuator端点配置、自定义探针与状态语义详解

发布时间:2026/10/13 13:18:08 来源:尧图企业网站定制
SpringBoot 接入健康检查最核心的一件事就是让外部系统能访问到你的 /actuator/health 状态。做后端服务的同学多少都碰过这种场景应用进程还活着日志还在滚客户已经开始反馈接口超时可负载均衡器还在把流量往这台机器上送。原因多半是健康检查没接入或者接了个寂寞。这篇文章只讲落地怎样最小化配置、自定义检查逻辑、区分存活与就绪以及我踩过的那些配置坑。适合刚接手服务部署、正在排查监控告警的同学参考。1. 健康检查到底在查什么先理解状态语义1.1 三类调用方三种健康语义健康检查不是单一 JSON而是多个系统的共同约定。常见调用方有三类容器编排平台要判断容器需不需要重启服务注册中心要判断实例能不能注册、要不要保留网关和负载均衡要判断流量要不要摘掉。这三方对“健康”的理解其实不一样。重启维度关心的是进程是否卡死注册维度关心的是应用能否加入服务列表流量维度关心的则是当前服务能不能处理请求。Spring Boot 的健康检查端点正是尽量把这三类语义统一到一个入口但真正要满足不同平台的要求还是需要把检查维度拆细。以容器编排场景为例很多团队刚上手时用一个 /health 同时配置存活探针和就绪探针结果一遇到数据库抖动存活探针直接把 Pod 杀掉数据库恢复后又得冷启动整个发布流程立刻变得很难看。把探针拆成 liveness幸存状态和 readiness就绪状态之后数据库抖动导致就绪探针失败但幸存探针仍通过Pod 不会被顺带重启。Spring Boot 2.3 之后提供了对应的健康检查分组后面会专门讲。1.2 健康状态不是一拍脑袋是各组件汇总投票Actuator 的 /health 返回的不是简单的“进程在”而是 Spring 容器里所有 HealthIndicator 的汇总结果。每个 indicator 负责检查一类资源比如数据源、消息队列、磁盘空间、自定义校验任务。汇总的规则由 HealthAggregator 决定默认是取“最差状态”只要有一个组件 DOWN整体就是 DOWN如果没有 DOWN整体才可能是 UP。打个比方餐厅只要厨房着火就没法营业即使前台灯亮着、服务员齐整最终门上也该挂出“暂停营业”。默认聚合器就是这种悲观策略宁可错杀不可漏放。所以看到 /health 返回 DOWN不要第一反应是“进程死了”而要先想“是不是某一个依赖组件状态不对”。这一条语义我建议刻在脑子里它能解释后面绝大多数疑难杂症。2. 最小化配置把 /actuator/health 真正暴露出来2.1 依赖引入Spring Boot 项目里接入健康检查第一步永远是引入 Actuator 依赖。在 pom.xml 里加这段就行dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependencySpring Boot 2.x 和 3.x 的 starter 名字都一样底层管理端点的机制略有调整但 health 端点用法基本一致。引入依赖后如果项目用的是默认端口和默认上下文本机访问 /actuator/health 通常已经能出结果。但很多场景下默认暴露范围不够尤其是探针、注册中心、网关都需要直接调用健康端点的时候必须显式配置否则部署到某个平台上还是会踩到路径或权限的坑。2.2 暴露范围与安全例外不同版本的 Spring Boot 对 Actuator 端点默认暴露策略有差异建议不要依赖默认值而是自己明确写出允许暴露哪些端点。最常见的配置是management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: when-authorized show-components: when-authorizedmetrics 不是健康检查必需的但排查问题时很有用。health、info 属于外部系统需要调用的端点。不要图省事把 include 配成*env、beans、mappings 这些端点暴露出去会让内部结构、配置信息、Bean 列表全被外人看到安全风险比较大。如果是内网临时调试可以用下面的配置但调完记得改回来management: endpoints: web: exposure: include: *注意*在 YAML 里最好加上引号避免个别解析器把它当锚点处理。这个看起来很小的坑真有人踩过。2.3 看返回结果别只盯着 status启动应用后一条 curl 就能看到结构curl http://localhost:8080/actuator/health返回示例{ status: UP, components: { diskSpace: { status: UP, details: { total: 536870912000, free: 10485760000, threshold: 10485760, exists: true } }, ping: { status: UP } } }这个结构是 Spring Boot 2.3 以后的格式components 表示各 health indicator 的单项结果。老版本可能只有 status没有 components。如果你想快速定位 DOWN 的原因强烈建议在本地把 show-components 打开返回里能看到是 diskSpace 还是哪个自定义组件挂了。生产环境有个习惯值得坚持健康检查返回的 details 信息宁可少而准不要多而杂。比如把数据库连接地址、磁盘路径、缓存前缀暴露出来内部监控可能很爽但一旦这个端点被内网其他服务无鉴权调用信息就外流了。所以 production 我一般用when-authorized并且给对外监控账号单独开权限。实在不行至少把show-details配成never只保留 status 字段。2.4 独立管理端口与上下文路径当业务端口和监控端口混在一起健康检查被频繁调用时会占用业务线程资源。如果服务流量比较大建议把 Actuator 的管理端口专门拆出来management: server: port: 8081 endpoints: web: base-path: /internal这样健康检查路径就变成http://localhost:8081/internal/health。配置了独立端口之后探针、注册中心、网关都要跟着改路径否则就会一直探测失败。另外如果业务端配置了server.servlet.context-path/api那/actuator/health可能会变成/api/actuator/health管理端口如果没有 context-path访问路径又不一样。这种路径不一致的问题往往是线上“健康检查 404”的第一大来源。3. 从自动装配到自定义健康指标实战一个检查逻辑3.1 自动装配了哪些内置检查项当 classpath 里有 DataSource、Redis、Mongo、RabbitMQ 等组件时Spring Boot 会自动注册对应的 HealthIndicator。比如数据连接池有 DataSourceHealthIndicator缓存组件有 RedisHealthIndicator这就导致你明明什么都没写/health 里却出现一堆组件状态。这些自动注册的 indicator 不需要手写但你必须知道一件事它是否悄悄检查了某些组件。有些服务本来不依赖某个中间件但误引了 starter项目启动后 health 多出一个 DOWN 项排查半天才发现是这个依赖自带的健康检查引起的。看当前容器里注册了哪些 indicator最直接的办法是看 /actuator/health 返回里的 components。线上不建议把 beans 端点暴露出去本地看一下没问题。3.2 核心接口与命名规律自定义健康检查都是在写一个 HealthIndicator。核心接口很简单public interface HealthIndicator { Health health(); }实际开发时通常是写一个Component类返回一个Health对象。命名上有规律假设 bean 名是upstreamQuotaHealthIndicator那它在 /actuator/health 里的 component 键就是upstreamQuota会自动去掉HealthIndicator后缀。名字起得要有辨识度不然在 components 里看到一堆缩写根本分不清谁是谁。3.3 一个完整示例检查上游资源配额我这里写一个“检查上游资源配额是否还够”的指标很贴合现实。很多服务调用第三方接口之前都会先看本地配额如果配额没了接口一定会失败这种状态就应该反映到健康检查里。Component public class UpstreamQuotaHealthIndicator implements HealthIndicator { private final UpstreamQuotaClient quotaClient; public UpstreamQuotaHealthIndicator(UpstreamQuotaClient quotaClient) { this.quotaClient quotaClient; } Override public Health health() { try { QuotaInfo quota quotaClient.currentQuota(order-api); if (quota.getRemaining() 0) { return Health.down() .withDetail(reason, 剩余配额不足) .withDetail(quota, quota.getRemaining()) .build(); } return Health.up() .withDetail(quota, quota.getRemaining()) .build(); } catch (Exception e) { return Health.down() .withDetail(reason, 配额服务调用异常) .withDetail(error, e.getMessage()) .build(); } } }写这个类的关键点有三个一是不要把重逻辑塞进health()方法比如同步调一堆远程接口否则健康检查自己会把应用拖垮二是要设置超时不能因为一次配额服务超时就把健康检查线程卡死三是把有用的状态放进 details没意义的上下文不要塞。3.4 敏感信息控制与缓存 TTLshow-details的三个可选值never表示完全不返回详情when-authorized表示用户通过认证后才返回详情always表示任何人访问都返回详情。对外暴露的端点建议至少用when-authorized。如果健康检查被容器探针直接调用探针本身没有认证那就不能开when-authorized而应该用never否则探针只能拿到 status但也够用。另一个容易忽略的配置是缓存 TTLmanagement: endpoint: health: cache: time-to-live: 10s探针默认每 10 秒打一次如果每次打都去查一遍所有组件数据库和缓存都会白白增加压力。缓存 TTL 能有效降低检查频率但注意缓存会让状态延时最长滞后等于 TTL。高可用要求严格的场景TTL 不要配太大10 到 15 秒比较适中。4. 分组与探针配置把“活着”和“能接收流量”分开4.1 使用内置 Liveness 和 Readiness 分组Spring Boot 2.3 之后Actuator 支持健康检查分组。启用探针分组后/actuator/health/liveness和/actuator/health/readiness会独立返回。配置如下management: endpoint: health: probes: enabled: true health: livenessstate: enabled: true readinessstate: enabled: trueliveness 分组只关心应用本身是否还活着readiness 分组关心应用是否已经可以接收请求。这样就能做到数据库短时间抖动时readiness 返回 DOWN外部流量摘走而 liveness 仍然返回 UPPod 不会被重启。数据库恢复后readiness 自动变 UP流量重新进来。没有启用 probes 就直接访问/actuator/health/liveness大概率会是 404很多同学踩到过。4.2 结合容器编排平台配置探针如果服务部署在 KubernetesDeployment 里通常这样配spec: containers: - name: order-api image: order-api:2.1.3 ports: - containerPort: 8080 startupProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 20 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 30 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 0 periodSeconds: 10 timeoutSeconds: 3 successThreshold: 1 failureThreshold: 3 livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 10 periodSeconds: 15 timeoutSeconds: 3 failureThreshold: 3要注意的是liveness 探针的路径是/actuator/health/livenessreadiness 探针的路径是/actuator/health/readiness。如果你只有一个 /health永远无法实现“服务活着但暂时不接流量”的状态。4.3 探针参数别照抄要按服务能力调网上有很多探针模板但参数必须根据服务实际启动速度和依赖长短来调。比如一个会等待注册中心同步的服务启动可能要 60 秒如果 livenessProbe 的failureThreshold * periodSeconds小于 60Pod 就会在启动阶段被杀。我建议先做一次压测记录从进程启动到/actuator/health/readiness返回 UP 的时间再按这个时间乘 1.5 配置 startupProbe 的failureThreshold。还有 HTTP 状态码问题探针访问 404 时一般会算作失败。如果给 Actuator 配了独立管理端口探针的 port 要指向那个管理端口并且 path 也要同步否则就会一直失败。这种问题在测试环境往往不容易发现因为本地访问的业务端口直接通上了平台才暴露。5. 联动运维体系注册中心和网关的健康检查5.1 注册中心里的状态剔除与恢复服务注册中心默认靠心跳续约判断实例是否可用心跳超时会摘除实例。很多团队为了更快感知故障会把健康检查也接入注册逻辑让注册中心定期回调服务的 /actuator/health发现 DOWN 就立刻将实例标记为不可用。这个配置通常在服务注册客户端里做不同注册中心实现方式略有差异但核心思路一样把 Actuator 的 health 端点作为注册中心探测源。要特别注意注册中心的健康检查会影响服务发现如果某个实例因为依赖组件 DOWN 被摘掉调用方会自动把流量打到剩余实例上。这是好事但前提是每个实例的 health 检查项一致否则可能出现状态分裂同一个服务一半实例 UP一半 DOWN流量压到剩余实例上反而把剩余实例也压垮。5.2 网关按健康状态摘流量网关层一般不会每个请求都调用一次 /health而是维护一份可用服务列表。常见做法是网关从注册中心拿到实例再通过注册中心标记的 UP/DOWN 状态过滤路由。如果某些网关需要主动探测也可以在路由转发前用本地缓存每隔几秒同步后台健康状态没必要让每个请求都阻塞等一次远程 health。这块我能给的落地建议是先让注册中心负责实例上下线网关只负责按列表转发如果服务没有注册中心再考虑网关内建探活。健康检查的调用链越简单越不容易把自己也搞挂。6. 常见问题与排查技巧实录6.1 /actuator/health 404 了怎么办按照顺序排查依赖引入没有spring-boot-starter-web 或 spring-boot-starter-webflux 在不在management 是否配了独立端口上下文路径有没有影响exposure.include 里是否包含 healthSpring Boot 版本是不是老到你根本不认识这个路径。最常踩的是同时设置了server.servlet.context-path/api却没同步给 management导致访问路径变成/api/actuator/health。这种问题用下面的速查表定位很快现象可能原因排查方式访问 /actuator 404未引入 actuator 或未暴露检查依赖、检查 exposure.include/health 404 但 /actuator 可访问health 未 include 或 context-path 干扰检查配置访问完整路径Spring Boot 1.x 项目端点路径不是 /actuator/health确认项目版本按 1.x 规则处理管理端口分离健康检查不在业务端口访问 management.server.port 对应端口6.2 状态一直是 DOWN 但服务正常先打开 show-components看是哪个组件在拉低状态。常见原因有自定义 HealthIndicator 抛异常了某个依赖组件的连接串填了默认值但根本没连上磁盘剩余空间低于阈值。默认 diskSpace 阈值是 10MB本地测试目录剩余不足就会出现 DOWN。临时关闭磁盘空间检查可以这样配management: health: diskspace: enabled: false但最好先搞明白为什么磁盘不够。关闭检查只是止损不是修复。6.3 数据库健康检查过度敏感内置 DataSourceHealthIndicator 每次执行 validation query。如果连接池已满或数据库连接被防火墙瞬间切断health 会 DOWN流量被摘除。对核心服务DOWN 不是坏事但有些服务只是偶尔读写数据库却被数据库瞬时波动影响整体可用性。这种场景可以为服务设计“弱依赖”的健康逻辑比如把数据库检查失败记成 WARN不直接拉低整体状态。实操心法先看监控里数据库 DOWN 持续多久如果只有几百毫秒而且没有业务影响可以考虑在数据库检查逻辑里加一个短期缓存或阈值判断避免引起无谓的摘流量。健康检查是为业务服务的不是制造告警的。6.4 自定义检查状态被缓存升级后一直显示旧状态如果你自己写了一个状态缓存比如用 static 变量保存上次检查结果注意并发和失效条件。Actuator 的 cache TTL 配置只是对端点返回结果的缓存不会影响内部状态。我见过一个项目把当前服务和依赖的状态放在本地内存重启之后不失效导致 /actuator/health 一直返回 OUT_OF_SERVICE查了半天是自定义组件里一个常量没更新。建议自定义 indicator 每次都重新获取最小化状态重活交给异步线程池去刷新。6.5 配置优先级导致配置不生效Spring Boot 的配置来源很多命令行参数、系统环境变量、application.yml、配置中心有严格优先级顺序。如果你在运维平台配了环境变量又想在 application.yml 里覆盖大概率不生效。我建议健康检查相关配置统一用环境变量或配置中心管理并且每个配置项都要有明确的所有者。排查健康检查问题有时候先查“读到的配置到底是什么”比直接改代码更快。可以先在 Actuator 的 env 端点里临时看某个属性值但记得不要在生产长期暴露 env 端点。6.6 排查工具清单最后给一份平时排查健康检查的工具组合先用 curl 手动调用 /actuator/health看原始 JSON再用 show-components 定位 DOWN 的具体组件然后用 jq 提取字段配合 watch 观察状态变化最后用容器平台的探针日志确认请求是否成功。不要一上来就查代码先根据返回结构把问题缩小到某个组件很多坑都是配置层面的。如果哪天 /actuator/health 又被别的东西搞挂了先冷静看看返回 JSON而不是盲改配置。健康检查这块配置看着简单真正要跑得稳靠的是把状态语义、探针参数和依赖关系都想清楚。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑