目录导读
- 为什么需要健康检查?——微服务架构的“体检表”
- 健康检查的核心设计原则(含必应/谷歌SEO关键词布局)
- Java实现方案对比:Spring Boot Actuator vs 自研轻量探针
- 手写案例:基于Spring Boot + Redis + MySQL的存活与就绪检查
- 高级技巧:依赖隔离、缓存穿透保护与优雅停机
- 常见问题FAQ(问答环节)
- 压轴总结:生产环境健康检查的黄金法则
精讲

为什么需要健康检查?——微服务架构的“体检表”
在分布式系统中,服务实例可能因网络抖动、内存溢出、下游依赖故障而“假死”,健康检查(Health Check)是负载均衡、服务发现和自动恢复的基石。据统计,超过73%的生产事故可通过有效的健康检查提前拦截,对Java后端而言,健康检查不仅是/health端点,而是一套可观测性与自愈能力的闭环。
健康检查的核心设计原则(含SEO关键词布局)
- 明确语义区分:存活探针(Liveness) 判断进程是否需要重启;就绪探针(Readiness) 判断流量是否可进入。
- 快速响应:健康检查接口应<100ms,避免触发超时雪崩。
- 依赖检查分级:强依赖(数据库)失败必须置为DOWN;弱依赖(缓存)可降级为WARN。
- 资源开销最小化:禁止在健康检查内做重IO操作。
Java实现方案对比:Spring Boot Actuator vs 自研轻量探针
| 方案 | 优势 | 劣势 |
|---|---|---|
| Actuator | 开箱即用,支持/actuator/health,可聚合Redis、DB、MQ健康状态 |
依赖spring-boot-starter-actuator,配置略重 |
| 自研Servlet | 极轻量、完全可控,适合非Spring生态 | 需手动处理线程池、连接池监控 |
SEO要点:本段重点覆盖「Java健康检查框架对比」「Spring Boot actuator health」等高频检索词。
手写案例:基于Spring Boot + Redis + MySQL的存活与就绪检查
@Component
public class HealthCheckHandler {
@Autowired
private DataSource dataSource;
@Autowired
private StringRedisTemplate redisTemplate;
@GetMapping("/health")
public ResponseEntity<HealthStatus> check() {
HealthStatus status = new HealthStatus();
// 1. 存活检查:JVM堆内存是否用尽
status.setLiveness(checkHeapMemory());
// 2. 就绪检查:数据库连接 + Redis ping
status.setReady(checkDbAndRedis());
return status.isReady()
? ResponseEntity.ok(status)
: ResponseEntity.status(503).body(status);
}
private boolean checkDbAndRedis() {
try {
// 数据库连接池快速验证(避免全量查询)
dataSource.getConnection().isValid(2);
// Redis PING(使用连接借出,不额外开线程)
return "PONG".equals(redisTemplate.execute(conn -> conn.ping()));
} catch (Exception e) {
return false;
}
}
private boolean checkHeapMemory() {
double used = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
double max = Runtime.getRuntime().maxMemory();
return used / max < 0.90; // 堆使用率>90%视为不健康
}
}
代码解读:isValid(2) 是JDBC自带超时检测,比每次执行SELECT 1更高效,Redis使用execute(conn -> conn.ping())避免创建新连接。
高级技巧:依赖隔离、缓存穿透保护与优雅停机
- 依赖隔离:使用
CircuitBreaker(如Resilience4j)包裹健康检查调用,防止下游慢接口拖垮探针线程池。 - 缓存穿透保护:健康检查结果缓存2-3秒,防止高并发下触发DB连接风暴。
- 优雅停机:在
@PreDestroy中标记服务为DOWN,并等待在途请求完成(Spring Boot 2.3+已支持graceful shutdown)。
@Bean
public HealthStatusHolder healthStatusHolder() {
// 预热缓存,初始为UP
}
常见问题FAQ(问答环节)
Q1:健康检查返回503后,K8s多久会终止Pod?
A:取决于kubelet的failureThreshold(默认3次)和periodSeconds(默认10s),约30秒后触发容器重启。
Q2:为什么我的/actuator/health总是显示UP,但流量却不可用?
A:可能只配了Liveness探针,而没配Readiness,需通过management.endpoint.health.probes.enabled=true开启分组,并分别映射到/health/liveness和/health/readiness。
Q3:健康检查内是否可以做远程调用? A:严格禁止!健康检查必须本地化、轻量化,若需验证下游,采用异步任务在后台进行,仅保留最近一次的探测结果。
Q4:自研健康检查如何避免线程泄漏?
A:使用ScheduledExecutorService单线程调度,并设置setRemoveOnCancelPolicy(true),避免每次请求新建线程。
压轴总结:生产环境健康检查的黄金法则
- 三条底线:① 快(<100ms)② 隔离(不因探针拖垮业务)③ 明确(区分存活/就绪)。
- 可观测闭环:健康检查结果需暴露为Prometheus指标(
health_check_status),便于Grafana联动告警。 - 双保险:除了HTTP端点,建议额外开启TCP端口探针(如
ServerSocket监听),防止Web容器阻塞导致HTTP探针失效。
最后嘱咐:健康检查不是“写一次就完事”,要随业务依赖变更持续编排,在Java微服务中,它既是“门卫”,也是“手术灯”——让系统更坚强,让故障更透明。