Spring Boot实现健康检查案例:从基础到生产级实战指南
目录导读
- 为什么需要健康检查? —— 微服务架构中的“生命体征”
- Spring Boot Actuator基础 —— 快速搭建第一个健康端点
- 自定义健康指示器 —— 让检查更懂你的业务
- 生产级健康检查策略 —— 数据库、缓存、消息队列的深度探活
- 结合K8s与负载均衡器的实战配置 —— 从本地到云原生
- 常见问题与最佳实践问答 —— 避开那些坑
为什么需要健康检查?
在微服务架构中,一个服务“启动”并不等于“可用”,当你的Spring Boot应用依赖外部组件(如MySQL、Redis、Kafka)时,应用进程虽然活着,但可能因连接池耗尽、下游服务不可达而无法处理请求,健康检查(Health Check)就是向外部系统(如Kubernetes、Consul、负载均衡器)报告服务真实可用状态的机制。

据Google SRE的实践统计,60%以上的线上故障源于“假死”服务——进程存活但业务不可用,健康检查能帮助编排系统自动摘除异常实例,实现自愈。
Spring Boot Actuator基础
1 依赖引入
只需在pom.xml中加入:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2 暴露健康端点
在application.yml中配置:
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always # 生产环境建议改为when-authorized
启动后访问 http://localhost:8080/actuator/health,你会得到:
{"status":"UP"}
这就是最基础的“存活”检查(Liveness)。
自定义健康指示器
内置的HealthIndicator只能检查应用本身,但实际业务需要检查关键依赖,我们可以实现HealthIndicator接口:
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Override
public Health health() {
try {
// 模拟数据库连接测试
boolean isDbUp = checkDatabaseConnection();
if (isDbUp) {
return Health.up()
.withDetail("database", "MySQL")
.withDetail("latency_ms", 23)
.build();
}
return Health.down()
.withDetail("error", "Connection refused")
.build();
} catch (Exception e) {
return Health.down(e).build();
}
}
}
此时健康端点会聚合多个指示器的结果:
{
"status": "DOWN",
"components": {
"db": {"status": "UP"},
"diskSpace": {"status": "UP"},
"databaseHealth": {"status": "DOWN", "details": {"error": "Connection refused"}}
}
}
生产级健康检查策略
1 区分“存活”与“就绪”
Kubernetes中需要两种探针:
- Liveness探针:判断进程是否需要重启(如死锁、内存溢出)
- Readiness探针:判断是否可接收流量(如依赖未就绪、连接池未满)
Spring Boot 2.3+支持分组健康检查:
management:
endpoint:
health:
group:
liveness:
include: ping,diskSpace
readiness:
include: db,redis,rabbit
暴露后:
/actuator/health/liveness→ 仅检查基础状态/actuator/health/readiness→ 检查全链路依赖
2 深度探活:不要只做TCP连接
以MySQL为例,正确做法是执行SELECT 1,而非仅Connection.isValid(0),同样,Redis应执行PING,Kafka应尝试获取元数据,Spring Boot已内置了DataSourceHealthIndicator,但默认只做isValid(),如果连接池满了,可能仍误报UP,建议覆盖重写。
3 超时与缓存
健康检查不应拖慢主线程,使用@Scheduled异步刷新健康状态:
@Component
public class CachedHealthIndicator implements HealthIndicator {
private volatile Health cachedHealth = Health.unknown().build();
@Scheduled(fixedRate = 5000)
public void refresh() {
cachedHealth = doRealCheck();
}
@Override
public Health health() {
return cachedHealth;
}
}
结合K8s与负载均衡器的实战配置
1 K8s探针配置示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
2 负载均衡器(如Nginx)配置
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
server {
location /health {
proxy_pass http://backend/actuator/health;
}
}
配合max_fails与fail_timeout自动摘除异常节点。
3 外部系统监控
将/actuator/health接入Prometheus + Grafana,定义告警规则:up == 0持续5分钟则触发告警。
常见问题与最佳实践问答
Q1:健康检查总是返回DOWN,但应用实际能工作?
A:多数情况是依赖组件(如Redis)在健康检查时网络抖动,或DataSource超时设置过短,建议:
- 增加超时时间,如
spring.datasource.hikari.connection-timeout=3000 - 使用
@ConditionalOnEnabledHealthIndicator关闭不必要的指示器
Q2:健康检查会影响性能吗? A:高频探活(如每1秒)会打满连接池,解决方案:
- 降低频率(生产建议10秒以上)
- 使用异步缓存健康状态,如上文
CachedHealthIndicator
Q3:如何处理不对外暴露的端口?
A:如果服务只在内网,通过management.server.port: 9090单独暴露端口,并通过ACL限制访问。
Q4:多环境配置如何管理? A:使用Spring Profiles:
management:
endpoint:
health:
show-details: ${HEALTH_DETAILS:never} # dev环境可设为always
Q5:自定义检查项如何优雅降级?
A:在health()返回Health.status(new Status("WARN")),K8s仍视为UP,但监控系统可区别告警。
健康检查是微服务可观测性的第一道防线,从简单的/actuator/health到自定义深度探活,再到容器编排集成,每一步都直接影响系统的自愈能力与稳定性,记住核心原则:健康检查不是“它活着吗”,而是“它还能正常履行职责吗”,建议根据业务依赖绘制一张依赖清单,为每个关键组件编写对应的HealthIndicator,并配置合理的超时与缓存策略,最终在K8s环境中同时使用liveness与readiness,你会发现服务的故障恢复时间从“分钟级”缩短到“秒级”。
(全文完)