在云原生与Kubernetes(K8s)主导的部署环境中,传统的“进程是否在运行”的健康检查已远远不够。Spring Boot 2.3+通过引入Spring Boot livenessState 和 readinessState这两个核心的应用程序状态,将应用健康度细化为“应用是否存活”与“应用是否就绪”两个维度。理解并正确配置它们的核心价值在于,它们能与K8s的探针(Probe)机制无缝集成,实现故障Pod的自动重启与滚动更新时的零宕机流量切换,是构建高可用、可自愈微服务系统的基石。
一、 从单一健康检查到“存活”与“就绪”的分离

在传统模式下,负载均衡器或监控系统通常只检查一个健康端点(如/health)。如果该端点返回不健康,实例会被直接踢出服务池。但这存在一个明显问题:一个应用可能因为暂时无法处理请求(如启动中、加载缓存、依赖的数据库暂时过载)而健康检查失败,但这并不意味着应用进程本身“死了”。粗暴地重启或移除它可能适得其反。
Spring Boot livenessState 和 readinessState正是为了解决这一困境而设计。它们对应了K8s中两个关键的健康探针概念:
- 存活探针 (Liveness Probe): 用于判断容器是否“活着”。如果失败,K8s认为容器已处于不可恢复的“死亡”状态(如死锁、内存泄漏导致进程僵死),会重启该Pod以尝试恢复服务。
- 就绪探针 (Readiness Probe): 用于判断容器是否“准备好”接收外部流量。如果失败,K8s认为容器暂时无法处理请求(如启动初始化未完成、依赖服务不可用),会将该Pod从Service的负载均衡端点中移除,直到其就绪探针通过,期间不会重启Pod。
这种分离是微服务弹性设计的关键。在“鳄鱼java”的云原生课程中,我们强调:混淆这两种状态是导致K8s中应用被不当重启或流量中断的最常见原因之一。
二、 深入解读:LivenessState(存活状态)
LivenessState 表示应用程序内部状态是否健康,能否继续运行。它关注的是应用进程本身的“死活”。
核心特征:
- 失败意味着“不可恢复”:通常表示应用遇到了无法通过等待或重试自动修复的内部致命错误。
- 触发的动作是“重启”:在K8s中,Liveness Probe失败会导致Pod被杀死并重新创建。
- 状态是二元的:在Spring Boot中,它被抽象为LivenessState.CORRECT(健康)和LivenessState.BROKEN(不健康)。
典型场景:
1. 死锁:关键线程池全部阻塞,应用失去响应能力。
2. 关键内部资源耗尽:如堆外内存(Direct Buffer Memory)泄漏,且无法通过GC回收。
3. 应用内部组件的不可恢复故障:例如,核心的消息处理循环因无法处理的异常而永久停止。
你可以通过ApplicationAvailability接口在代码中获取或更新此状态,但通常不推荐手动将其置为BROKEN,除非你非常确定遇到了必须通过重启才能解决的内部故障。应用本身更擅长通过崩溃(Crash)来间接表示存活性失败。
三、 深入解读:ReadinessState(就绪状态)
ReadinessState 表示应用程序是否准备好处理外部请求(通常是HTTP流量)。它关注的是应用的“服务能力”。
核心特征:
- 失败意味着“暂时不可用”:通常是由于外部依赖不可用或应用正在进行初始化、维护。
- 触发的动作是“隔离流量”:在K8s中,Readiness Probe失败会从Service的Endpoints列表中移除该Pod,停止向其发送新请求,但Pod本身继续运行。
- 状态是动态的:在Spring Boot中,状态为ReadinessState.ACCEPTING_TRAFFIC(就绪)和ReadinessState.REFUSING_TRAFFIC(未就绪)。这个状态在应用生命周期中可以多次切换。
典型场景:
1. 应用启动阶段:Spring上下文正在加载,Bean尚未初始化完成。
2. 依赖服务失联:数据库、Redis、下游核心微服务暂时无法连接。
3. 负载过高:应用自身认为当前负载已超过其处理能力,需要主动拒绝部分流量进行自我保护(类似熔断)。
4. 执行管理任务:正在进行批量数据迁移、缓存预热等,期间希望暂停服务。
这是Spring Boot livenessState 和 readinessState中开发者交互更多、更主动管理的一个状态。
四、 实战配置:在Spring Boot和K8s中启用与集成
步骤1:在Spring Boot中启用与暴露状态端点
从Spring Boot 2.3开始,只需简单配置即可。
# application.yml
management:
health:
livenessstate:
enabled: true # 启用存活状态探测
readinessstate:
enabled: true # 启用就绪状态探测
endpoint:
health:
probes:
enabled: true # 关键:为K8s提供专用的探针端点
show-details: always # 可选,详情显示
endpoints:
web:
exposure:
include: health # 暴露统一的健康端点,其下包含子端点
配置后,Spring Boot会提供三个关键的HTTP端点:
- /actuator/health/liveness: 专门用于K8s存活探针,反映LivenessState。
- /actuator/health/readiness: 专门用于K8s就绪探针,反映ReadinessState。
- /actuator/health: 聚合端点,同时显示两者及传统健康指标。
步骤2:在K8s Deployment中配置探针
将Spring Boot的端点与K8s探针关联是核心。
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: my-springboot-app
image: myapp:latest
ports:
- containerPort: 8080
# 存活探针配置
livenessProbe:
httpGet:
path: /actuator/health/liveness # 指向存活端点
port: 8080
initialDelaySeconds: 60 # 给应用足够的启动时间
periodSeconds: 10 # 每10秒检查一次
failureThreshold: 3 # 连续失败3次判定为失败
timeoutSeconds: 2 # 探针请求超时时间
# 就绪探针配置
readinessProbe:
httpGet:
path: /actuator/health/readiness # 指向就绪端点
port: 8080
initialDelaySeconds: 30 # 可以比存活探针稍短
periodSeconds: 5 # 检查频率可以更高,以便快速响应状态变化
failureThreshold: 2
timeoutSeconds: 2
在“鳄鱼java”的K8s部署模板中,这是每个Spring Boot应用的标配。
五、 高级应用:自定义就绪状态与真实案例
真正的威力在于根据业务逻辑动态控制ReadinessState。
案例:依赖数据库的核心服务
假设一个用户服务严重依赖数据库。当数据库连接池中所有连接都因网络闪断而失效时,应用进程依然活着(Liveness健康),但已无法处理任何业务请求。此时,应主动将就绪状态置为REFUSING_TRAFFIC。
@Component public class DatabaseHealthIndicator implements ApplicationRunner {private final ConnectionPool connectionPool; private final ApplicationEventPublisher eventPublisher; public DatabaseHealthIndicator(ConnectionPool pool, ApplicationEventPublisher publisher) { this.connectionPool = pool; this.eventPublisher = publisher; } @Override public void run(ApplicationArguments args) { // 启动一个后台线程或使用调度任务定期检查 ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(() -> { boolean isDbHealthy = checkDatabaseConnection(); // 自定义检查逻辑 if (!isDbHealthy) { // 发布状态变更事件,将应用标记为“未就绪” AvailabilityChangeEvent.publish(this.eventPublisher, this, ReadinessState.REFUSING_TRAFFIC); // 同时可以记录日志、发送告警 } else { // 数据库恢复,标记为“就绪” AvailabilityChangeEvent.publish(this.eventPublisher, this, ReadinessState.ACCEPTING_TRAFFIC); } }, 0, 30, TimeUnit.SECONDS); // 每30秒检查一次 }
}
当readiness端点返回{"status": "OUT_OF_SERVICE"}时,K8s探针失败,Pod被移出负载均衡,避免了用户看到大量数据库错误。同时,运维人员有时间窗口去排查数据库问题,而不会被频繁重启的应用日志所干扰。
六、 避坑指南:常见误用与最佳实践
误区1:将外部依赖检查放入Liveness Probe
错误:因为Redis连不上,就将存活状态置为BROKEN,导致K8s不断重启Pod。
正确:外部依赖失败应影响Readiness,而不是Liveness。重启Pod解决不了网络或Redis本身的问题。
误区2:探针检查过于复杂或耗时
探针检查应该轻量、快速、无副作用。避免在探针检查中执行复杂SQL查询或远程调用。通常检查内部状态或一个简单的连接测试即可。配置合理的timeoutSeconds。
误区3:initialDelaySeconds设置过短
应用启动需要时间。如果探针在Spring上下文未完全初始化前就开始检查,会导致启动失败。根据应用启动时间合理设置,通常存活探针的初始延迟应大于就绪探针。
最佳实践总结:
- 严守定义:Liveness = 内部致命故障,需重启;Readiness = 临时不可用,需隔离流量。
- 默认启用:在新项目中,默认开启
management.health.probes.enabled。 - 结合优雅停机:在应用关闭时,
ReadinessState会自动变为REFUSING_TRAFFIC,与server.shutdown=graceful配合实现完美下线。 - 监控与告警:监控就绪状态频繁切换的事件,这往往是依赖不稳定或应用性能问题的早期信号。
总结与思考
Spring Boot livenessState 和 readinessState 不仅仅是一次技术升级,它代表了运维思维从“管理机器进程”到“管理应用服务状态”的深刻转变。通过将健康状态一分为二,并与编排系统深度集成,我们赋予了应用自我声明和自我保护的能力。
请审视你的Spring Boot应用:是否还在使用一个简单的/health端点应付所有场景?在K8s中滚动更新时,是否仍有少量请求失败?当数据库抖动时,用户是否直接看到错误页面,而不是一个优雅的降级处理?正确理解和应用存活与就绪状态双检机制,就是构建一个能够应对故障、平滑伸缩的韧性系统的关键一步。从今天起,让你的应用学会告诉外界:“我还活着,但我现在需要静一静。”
版权声明
本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。





