本文目录导读:

- 核心概念:liveness(存活)和 readiness(就绪)
- 方案一:基于Spring Boot Actuator(最推荐,最成熟)
- 方案二:手动实现Servlet/Filter(适合非Spring项目或极简场景)
- 方案三:使用Micrometer + 非Spring应用(现代微服务)
- 方案四:针对RESTful API框架(如JAX-RS)
- 生产环境实战示例(Kubernetes + Spring Boot)
在Java中实现“服务健康”,核心是提供一个健康检查端点(Health Check Endpoint),这个端点需要能够反映当前应用实例是否正常运行,以及其依赖的外部资源(如数据库、Redis、其他微服务)是否可用。
以下是从简单到复杂、从手动到自动的几种实现方式及最佳实践。
核心概念:liveness(存活)和 readiness(就绪)
在实现健康检查时,通常区分两个状态:
- 存活(Liveness):应用进程是否还活着?它是否在不断地重启或死锁?如果失败,容器编排平台(如K8s)会重启Pod。
- 就绪(Readiness):应用是否准备好接受流量?应用启动时是否还在加载缓存?数据库连接是否就绪?如果失败,K8s会将流量路由到其他健康的实例。
基于Spring Boot Actuator(最推荐,最成熟)
对于绝大多数基于Spring Boot的项目,这是最标准、最强大的方式,几乎无需手写代码。
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置暴露的端点(application.yml):
management:
endpoints:
web:
exposure:
include: health,info,metrics # 暴露health端点
endpoint:
health:
show-details: always # 显示详细健康信息(谨慎在生产环境开启,会暴露依赖信息)
访问健康检查端点:
-
GET /actuator/health返回:{ "status": "UP" } -
如果
show-details: always,会返回更详细的信息:{ "status": "UP", "components": { "db": { "status": "UP", "details": { "database": "MySQL", "result": 1 } }, "redis": { "status": "UP" }, "diskSpace": { "status": "UP", "details": { "total": 499963170816, "free": 228747804672, "threshold": 10485760 } } } }
自定义健康指示器:
如果应用依赖了Actuator无法自动检测的资源(如某个消息队列),可以自定义HealthIndicator:
@Component
public class MyCustomHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 模拟检查某个外部服务的逻辑
boolean serviceIsUp = checkSomeExternalService();
if (serviceIsUp) {
return Health.up()
.withDetail("service_name", "ExternalService")
.withDetail("status", "All good")
.build();
} else {
return Health.down()
.withDetail("service_name", "ExternalService")
.withDetail("error_message", "Service unreachable")
.build();
}
}
private boolean checkSomeExternalService() {
// 调用外部服务的健康检查接口或发送心跳
try {
// ... 实现检查逻辑
return true;
} catch (Exception e) {
return false;
}
}
}
配置活跃性/就绪性状态: Spring Actuator原生支持Kubernetes的存活和就绪探针:
management:
endpoint:
health:
probes:
enabled: true # 启用liveness和readiness探针
health:
readinessstate:
enabled: true
livenessstate:
enabled: true
/actuator/health/liveness:返回“存活”状态(一般只检查应用内部状态,比如是否死锁)。/actuator/health/readiness:返回“就绪”状态(检查数据库、消息队列等外部依赖是否可用)。
手动实现Servlet/Filter(适合非Spring项目或极简场景)
如果项目不依赖Spring Boot,或者想完全自己控制返回格式,可以手动实现一个Servlet。
实现自定义Servlet:
@WebServlet("/health")
public class HealthCheckServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
boolean isHealthy = true;
StringBuilder details = new StringBuilder();
// 1. 检查数据库连接
try (Connection conn = DataSourceUtil.getConnection()) {
if (conn.isValid(3)) { // 超时3秒
details.append("db: UP; ");
} else {
isHealthy = false;
details.append("db: DOWN; ");
}
} catch (Exception e) {
isHealthy = false;
details.append("db: ERROR (").append(e.getMessage()).append("); ");
}
// 2. 检查关键缓存(如Redis)
// ...
// 3. 返回响应
resp.setContentType("application/json");
resp.setStatus(isHealthy ? HttpServletResponse.SC_OK : HttpServletResponse.SC_SERVICE_UNAVAILABLE);
resp.getWriter().write(
"{\"status\": \"" + (isHealthy ? "UP" : "DOWN") + "\", \"details\": \"" + details.toString() + "\"}"
);
}
}
配置Servlet(web.xml或注解):
<servlet>
<servlet-name>health</servlet-name>
<servlet-class>com.example.HealthCheckServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>health</servlet-name>
<url-pattern>/health</url-pattern>
</servlet-mapping>
优点:完全可控,不依赖框架。 缺点:需要开发者自己处理线程安全、超时、异常等。
使用Micrometer + 非Spring应用(现代微服务)
如果项目使用了Micrometer(多用于Metric),也可以直接通过Micrometer注册健康指标。
MeterRegistry registry = new SimpleMeterRegistry();
// 注册一个Gauge来反映健康状态
Gauge.builder("health.status", () -> checkSystemHealth() ? 1 : 0)
.description("1 if healthy, 0 if unhealthy")
.register(registry);
// 在HTTP处理函数中根据Gauge值返回不同状态码
针对RESTful API框架(如JAX-RS)
对于JAX-RS(Jersey/RESTEasy)项目:
@Path("/health")
@Produces(MediaType.APPLICATION_JSON)
public class HealthResource {
@GET
public Response getHealth() {
HealthStatus status = new HealthStatus();
status.setStatus("UP");
status.addDetail("memory", Runtime.getRuntime().freeMemory());
// 可以检查其他依赖...
return Response.ok(status).build();
}
}
无论选择哪种方式,都建议遵循以下原则:
- 主动检查依赖:健康检查不应只返回“活着的”状态,而应主动检查关键的依赖(数据库、缓存、消息队列等)。
- 设置超时:检查外部依赖时,必须设置合理的超时时间(如3秒),避免健康检查线程阻塞导致整个应用卡死。
- 区分Liveness和Readiness:
- Liveness(存活):只检查应用内部状态,不依赖外部资源(如内存溢出、死锁),如果Liveness失败,K8s重启Pod。
- Readiness(就绪):检查外部依赖是否可用,如果Readiness失败,K8s停止路由流量,但不会重启Pod。
- 轻量级:健康检查端点应尽量轻量,避免执行复杂的业务逻辑或大量数据库查询,如果需要更详细的检查,可以考虑不同的端点(如
/health/detailed)。 - 自动注册:在微服务架构中,推荐使用服务注册中心(如Consul、Eureka、Nacos)配合健康检查,如果健康检查失败,服务会自动从注册中心摘除。
- 安全性:健康检查端点可能暴露内部依赖信息,建议内部网络使用或添加认证(如Spring Actuator的
management.endpoint.health.show-details=when-authorized配合Spring Security)。
生产环境实战示例(Kubernetes + Spring Boot)
假设你有一个Spring Boot应用部署在K8s上,典型的配置如下:
Spring Boot (application.yml):
management:
endpoint:
health:
probes:
enabled: true
health:
readinessstate:
enabled: true
livenessstate:
enabled: true
Kubernetes Deployment (deployment.yaml):
spec:
containers:
- name: my-app
# ... 镜像配置
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
这样,K8s就能:
- 当服务死锁时,重启Pod。
- 当数据库宕机时,将流量从该Pod移走(但Pod不重启),直到数据库恢复。
如果你的项目是Spring Boot,直接使用Actuator是最快的选择,如果是其他框架,先判断是否已有类似内置支持(如Quarkus的/q/health、Micronaut的/health),再考虑手动实现。