Actuator、健康状态与优雅停机:实例何时该接收流量
进程活着、端口能连、/actuator/health 返回 200,并不能证明实例应该接收流量。数据库短暂失败时重启全部实例可能形成雪崩;管理端口正常而业务 connector 已饱和,会制造假健康;停机时先关闭数据库池再摘流量,则在途请求必然失败。Actuator 的核心价值不是多几个 URL,而是把管理端点、组件健康、应用可用性和生命周期状态连接成受控运维面。
官方 Actuator Endpoints 区分 endpoint 的可用、访问与暴露,并定义 health、conditions、startup 等能力。生产配置应基于 Spring Boot 当前发行线核对默认值;特别是端点访问控制在不同代际发生过演进,不要照搬旧版 enabled 或 security 示例。
Endpoint 要经过可用、访问和技术暴露三道门
一个 endpoint 有 Bean 并不代表 HTTP 可访问。Boot 根据依赖和配置决定 endpoint 是否 available,access 决定允许 none/read-only/unrestricted 等操作级别,exposure 决定是否通过 Web 或 JMX 暴露。Web 暴露后再由基础路径、端口和安全链决定实际入口。
默认只暴露少量端点是安全边界。env、configprops、beans、mappings、loggers、heapdump 与 threaddump 都可能泄露配置结构、路径、类名、线程上下文或内存秘密。include=* 再依赖“内网安全”不是可靠策略;应按用途白名单、独立鉴权、网络限制、响应脱敏和访问审计。
管理端口分离可以隔离流量和安全策略,也可能让探针绕过主服务器故障。若管理 context 使用独立 connector,它健康不能证明业务端口可接受连接。liveness/readiness 的附加路径应根据平台放在主 server 上,或者额外检查主 connector 状态。
自定义 @Endpoint 操作是管理 API,同样需要输入上限、并发控制、超时和幂等。写操作能清缓存、改日志级别或触发任务时必须有强鉴权和审计;不要把任意 Bean 方法映射成 endpoint。返回对象还要避免序列化整个内部对象图。
Health 是组件状态聚合,不是万能业务真相
HealthContributor 可以是单个 indicator 或 composite。每个组件返回 Status 与 detail,StatusAggregator 按严重度得到整体状态,HttpCodeStatusMapper 再映射 HTTP 状态。自定义 status 时若只改顺序不改 HTTP mapping,平台可能仍把故障当 200;自定义 mapping 也可能覆盖默认 DOWN/OUT_OF_SERVICE 映射,需要显式保留。
HealthAggregationDemo.java 展示可选依赖失败不必杀死实例:
javac --release 17 -Xlint:all -Werror examples/backend-development/spring-boot/actuator-health-shutdown/AvailabilityTransitionDemo.java examples/backend-development/spring-boot/actuator-health-shutdown/HealthAggregationDemo.java
java -cp examples/backend-development/spring-boot/actuator-health-shutdown HealthAggregationDemocomponents={database=UP, optionalSearch=DOWN} liveness=CORRECT readiness=ACCEPTING_TRAFFIC模型表达依赖分级,不代表所有应用都应忽略搜索故障。如果搜索是核心路径,它应影响 readiness;若有可验证降级,则保留接流量并单独告警。健康分组要与业务能力对应,不能把所有 indicator 无差别塞进全局状态。
indicator 必须快速、有界、低副作用。每次探针执行昂贵全表查询或远程写,会由平台高频调用放大成新负载。缓存短周期结果时要标记新鲜度,避免依赖已恢复却长期 DOWN。慢 indicator 会被记录警告,但应用仍应设置自己的 timeout 和并发隔离。
detail 默认不应公开。即使值被脱敏,数据库产品、集群名、磁盘路径和异常消息也可能帮助攻击者。对平台只返回状态和必要 component id,详细证据进入受控日志/指标。健康检查本身失败要返回明确 UNKNOWN/DOWN,不得抛出让 endpoint 500 且无法分型。
Liveness 回答能否自愈,Readiness 回答能否接流量
ApplicationAvailability 保存 LivenessState 与 ReadinessState。启动时 liveness 从 BROKEN 到 CORRECT,readiness 在 Runner 完成前保持 REFUSING_TRAFFIC,ready 后进入 ACCEPTING_TRAFFIC;关闭时先拒绝流量。二者回答不同问题,不能共用一组依赖。
外部数据库、缓存或 API 通常不应影响 liveness。共享依赖故障时,如果所有实例 liveness 都失败,编排平台会重启全部实例,增加启动风暴而无法修复外部系统。只有应用内部已经不可恢复、重启有机会修复的状态才适合 liveness。
Readiness 是否包含外部依赖要按能力判断。共享数据库不可用时摘除所有实例会让入口快速失败,但继续接流量也可能耗尽线程;应结合上游熔断、降级、恢复行为和依赖是否为关键路径选择。Boot 默认不会自动把所有 health indicator 加入 readiness,正是为了避免替业务做这个决定。
AvailabilityTransitionDemo.java 固定正常启动与停机的流量状态:
java -cp examples/backend-development/spring-boot/actuator-health-shutdown AvailabilityTransitionDemoreadiness=[REFUSING_TRAFFIC, ACCEPTING_TRAFFIC, REFUSING_TRAFFIC] newRequestsAcceptedDuringDrain=false应用可以发布 AvailabilityChangeEvent 改变状态,但状态切换必须由明确组件拥有,并记录原因、版本和持续时间。多个组件互相发布 accepting/refusing 会产生最后写入者覆盖,最好由一个 availability coordinator 聚合条件。
优雅停机从拒绝新流量开始
收到 context close 后,Boot 把 readiness 切为 REFUSING_TRAFFIC,并让支持的 WebServer 执行 graceful shutdown:停止接受新请求,在限定时间等待活动请求完成,然后关闭服务器与 context。编排平台还需要时间发现 readiness 变化并停止转发,因此 termination 总预算必须包含传播余量。
关闭顺序应围绕入口和资源:先拒绝流量与停止任务入口,再等待在途请求/消费,最后关闭数据库、连接池、线程池和日志。若 SmartLifecycle phase 配错,依赖资源先停、消费者后停,会制造关闭期错误。每个阶段必须有最大等待,超时后记录未完成任务并执行明确强制策略。
长连接和异步请求需要特殊验证。SSE/WebSocket 是否在 grace period 内结束、客户端如何重连、处理中的命令是否幂等,都不能由服务器自动保证。后台消息消费者不属于 HTTP WebServer 的 active request,需独立 drain 协议。
进程可能收到强杀或节点故障,destroy 并非可靠提交点。业务状态必须在处理过程中持续持久化,outbox、幂等和任务租约负责恢复。优雅停机提高成功概率,不能升级成一致性保证。
Actuator 指标要能解释状态而不扩大基数
Actuator 与 Micrometer 提供 JVM、WebServer、HTTP、连接池等观测入口。指标名与 tag 组合构成时间序列,不能把用户 id、完整 URL、异常消息或动态配置值作为 tag。高基数会先压垮监控系统,再反过来拖慢应用。
健康状态适合用离散 gauge/事件记录,但告警不能只看当前值。readiness 在发布与停机阶段短暂 refusing 是正常状态,需要结合发布上下文与持续时间;liveness BROKEN、反复状态抖动、indicator 超时和 graceful shutdown 超预算应分别告警。
startup endpoint 依赖 ApplicationStartup recorder,conditions/env/configprops 适合临时诊断。生产长期暴露前要评估内存、计算和安全成本。heapdump 与 threaddump 应通过运维授权流程获取,不直接暴露公网;下载大文件还要限制并发与审计。
探针配置必须与应用状态机一起验收
启动慢时可用 startup probe 保护初始化期,readiness 阻止流量,liveness 负责不可恢复状态。failureThreshold × periodSeconds 定义容忍窗口,timeoutSeconds 必须覆盖健康端点正常上界又小于故障预算。探针过于激进会在冷启动或短抖动时反复杀进程,过于宽松则延迟摘除。
验收至少覆盖:正常启动状态序列;Runner 失败不 ready;关键本地资源失败影响 readiness;共享外部依赖失败不触发全体 liveness;管理端口健康但业务端口故障能被发现;关闭时先 refusing 再 drain;超时请求被终止;所有线程和端口最终释放。
生产证据包含状态变化原因、Health component 耗时、慢 indicator、探针调用量、管理端点拒绝/授权、在途请求数、graceful shutdown 剩余预算和强制终止数。只有这些信号能连接“平台为什么摘流量”与“应用内部发生了什么”。
Actuator 不是监控平台替代品,也不是给所有内部状态开 HTTP 后门。它提供一套应用内管理协议;通过最小暴露、健康分组、可用性状态机和有界停机把协议接入平台,实例才会在正确的时间接流量、在错误的时间退出流量。
