本文目录导读:

- 基础方法:TCP/UDP 探测
- 常用方法:HTTP/HTTPS 健康检查(应用层探测)
- 分布式环境:基于注册中心的心跳(如 Consul、Eureka)
- 容器 / 云原生环境:Kubernetes 探针(Liveness & Readiness)
- 针对特定协议的自定义心跳
- 如何设计一个可靠的心跳系统(实战建议)
- 不同场景的选择建议
服务心跳检测是一种用于监控服务可用性和健康状态的机制,它通过定期发送探测信号(心跳)来判断服务是否正常运行,以下是几种常见的实现方式,从简单到复杂,适用于不同场景:
基础方法:TCP/UDP 探测
最简单的方式是直接检查服务的端口是否开放。
- 工具:
telnet、nc(netcat)、nmap - 原理: 尝试建立 TCP 连接或发送 UDP 包,如果端口未监听,则服务可能已挂。
- 示例(检查 80 端口):
nc -z -w 5 192.168.1.100 80 echo $? # 返回 0 表示成功,非0表示失败
- 优点: 简单,成本低,不依赖应用协议。
- 缺点: 无法判断服务是否逻辑正常(比如端口活但服务卡死、数据库连接耗尽)。
常用方法:HTTP/HTTPS 健康检查(应用层探测)
这是当前微服务架构中最常用的方式,服务提供一个专用的健康检查端点(如 /health 或 /ping)。
- 协议: HTTP GET 请求。
- 响应体: 通常返回 JSON 格式的状态,
{"status": "UP", "database": "ok", "diskSpace": "ok"} - 工具:
curl、wget、uhttp或专门的健康检查库。 - 示例(使用 curl 探测 Spring Boot 服务):
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/actuator/health # 期望返回 200,否则认为不健康。 - 优点: 能反馈逻辑状态(可检查数据库、缓存的外部依赖是否正常)。
- 缺点: 对服务有侵入性(需实现端点),且可能被慢请求阻塞。
分布式环境:基于注册中心的心跳(如 Consul、Eureka)
在微服务或 Kubernetes 集群中,服务需要向注册中心上报心跳。
- 工作原理:
- 服务启动时向注册中心注册(如 Consul、Etcd、Nacos、Eureka)。
- 服务每隔固定的时间(如 5 秒或 30 秒)发送一次心跳包(通常是 HTTP PUT 或 gRPC 请求)。
- 注册中心如果超过一定时间(如 90 秒)未收到心跳,则自动将服务标记为“不健康”或“下线”。
- 示例(Consul 的 TTL(生存时间)检查):
服务端定期调用
PUT /v1/agent/service/deregister/:service_id来续签。 - 优点: 自动、去中心化,适合大规模集群。
- 缺点: 需要额外维护注册中心组件(如 Consul 集群)。
容器 / 云原生环境:Kubernetes 探针(Liveness & Readiness)
如果你用 Kubernetes 部署服务,K8s 提供了内置的两种探针:
- 存活探针(livenessProbe): 判断容器是否还活着,如果失败,K8s 会杀掉容器并重启。
- 就绪探针(readinessProbe): 判断服务是否准备好接收流量,如果失败,K8s 会将该 Pod 从 Service 的 Endpoints 中移除(流量不会打过来)。
- 支持三种检查方式:
httpGet:GET 一个 URL,检查 200-399 状态码。exec:在容器内执行一条命令,检查退出码是否为 0。tcpSocket:尝试连接端口。
- 优点: 完全自动化,与容器生命周期绑定。
- 缺点: 仅限 Kubernetes 平台。
针对特定协议的自定义心跳
有些协议自带心跳机制,开发时可以直接复用:
- 数据库: MySQL 用
SELECT 1或ping命令;Redis 用PING命令。 - 消息队列: RabbitMQ 有 AMQP 心跳帧;Kafka 有元数据请求。
- WebSocket/HTTP 长连接: 应用层每 30 秒发送一个
{"type":"ping"},对方回应{"type":"pong"}。 - gRPC: 可以利用 gRPC 健康检查协议(
grpc.health.v1.Health)。
如何设计一个可靠的心跳系统(实战建议)
[应用服务] ----(每5秒发送心跳)----> [健康检查服务 / 注册中心]
|
|(如果连续3次未收到心跳)
v
[报警系统] --> 短信/邮件/钉钉/电话
代码实现示例(Python 简易健康检查守护进程):
import requests
import time
SERVICE_URL = "http://localhost:8080/actuator/health"
FAIL_TIMES = 0
MAX_FAIL = 3
while True:
try:
resp = requests.get(SERVICE_URL, timeout=5)
if resp.status_code == 200:
print("Heartbeat OK")
FAIL_TIMES = 0
else:
FAIL_TIMES += 1
except Exception as e:
print(f"Request failed: {e}")
FAIL_TIMES += 1
if FAIL_TIMES >= MAX_FAIL:
print("SERVICE IS DOWN! Sending alert...")
# 调用报警接口 (如集成 Slack、Prometheus Alertmanager)
FAIL_TIMES = 0 # 防止重复报警
time.sleep(5)
不同场景的选择建议
| 场景 | 推荐方案 | 关键指标 |
|---|---|---|
| 单体应用 / 传统部署 | TCP 端口探测 + 简单的 HTTP GET /health |
简单,但只能检测端口 |
| 微服务(非 K8s) | 使用 Consul/Nacos/Eureka 的 TTL 心跳 + 应用健康端点 | 分布式自动故障转移 |
| Kubernetes 环境 | K8s livenessProbe + readinessProbe (httpGet) |
容器自动重启/负载隔离 |
| 需要深度依赖检查 | 自定义健康端点,检查数据库、缓存、消息队列等 | 真实反映业务可用性 |
| 高性能内部通信 | 自定义 TCP 心跳 / gRPC Health Check | 低延迟,低资源占用 |
核心建议:
- 不要只检查端口(进程可能僵死)。
- 超时时间要小心:心跳超时设置应大于服务正常的最大响应时间(避免误判)。
- 加入隔离逻辑:如果服务健康但依赖的下游(如数据库)挂了,也应标记为不健康。
- 避免级联风暴:心跳检查机制本身要有稳定性,不要因为监控挂掉导致整个系统雪崩。