2. 健康检查机制:心跳检测、探活、就绪检查的原理与实现

边缘节点的故障自愈,第一步是什么?

不是修复,是发现。

你想想看,节点都挂了,你还在那分析日志、调参数,那不是白忙活吗?所以,健康检查机制就是整个自愈体系的「眼睛」。没有它,后面所有的策略都是盲人摸象。

今天咱们就聊聊三种最核心的检查机制:心跳检测、探活、就绪检查。这三兄弟各有各的脾气,用错了地方,反而会添乱。

2.1 心跳检测(Heartbeat)—— 最朴素的「你还活着吗?」

心跳检测,说白了就是节点定期向中心报告:「我还活着,一切正常。」

我在项目中遇到过一种情况:某个边缘节点网络偶尔抖动,但业务其实没断。结果心跳超时,系统直接把它踢出集群了。后来我加了个「心跳容忍窗口」,允许丢两三次心跳再判定死亡,这才消停。

核心原理:
  • 节点每隔 N 秒发送一次心跳信号
  • 中心节点记录最后一次心跳时间
  • 如果超过 T 秒没收到,标记为「疑似故障」
  • 连续 M 次未收到,标记为「故障」

代码实现其实不复杂。我习惯用轻量级的 UDP 或简单的 HTTP GET 来做心跳。下面是一个伪代码示例:

// 边缘节点端
while (true) {
    sendHeartbeat("node-001", "alive");
    sleep(5); // 每5秒发一次
}

// 中心端
function checkHeartbeat(nodeId) {
    lastTime = getLastHeartbeatTime(nodeId);
    if (now() - lastTime > 15) { // 15秒没收到
        markSuspect(nodeId);
    }
    if (now() - lastTime > 30) { // 30秒没收到
        markFailed(nodeId);
        triggerAutoHeal(nodeId);
    }
}
我的经验:心跳间隔不要设得太短。5-10秒一次就够了。太频繁反而会增加网络负担,尤其边缘网络带宽有限。

2.2 探活(Liveness Probe)—— 进程活着不代表业务活着

心跳检测只能告诉你「进程在不在」,但进程活着不代表业务正常。

我曾经遇到一个案例:边缘节点的 Java 进程还在,但 JVM 已经 Full GC 卡死了,业务完全不可用。心跳还在发,但用户已经骂翻了天。

这就是探活要解决的问题。它会主动去检查进程内部的状态,比如:

  • 进程是否响应特定端口
  • 进程是否能处理简单的请求
  • 进程内部的关键线程是否还活着

在 Kubernetes 里,探活 Probe 是这样配置的:

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
  failureThreshold: 3

这段配置的意思是:容器启动后等10秒,然后每5秒访问一次 /healthz 接口。连续3次失败,K8s 就会重启这个容器。

注意:探活接口一定要轻量。别在 /healthz 里查数据库、调外部 API。我见过有人把业务逻辑写进健康检查接口,结果检查本身就把服务拖垮了。

2.3 就绪检查(Readiness Probe)—— 别把流量引给「半死不活」的节点

探活决定「要不要重启」,就绪检查决定「要不要转发流量」。

你想想看,一个节点刚启动,缓存还没预热,数据库连接池还没建好,这时候把用户请求打过去,那不是找死吗?

就绪检查就是干这个的。它告诉负载均衡器:「我准备好了,可以接客了。」

K8s 里的配置长这样:

readinessProbe:
  tcpSocket:
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10
  successThreshold: 1
  failureThreshold: 3

这个配置检查 8080 端口的 TCP 连接是否正常。一旦连续3次失败,K8s 就会把这个 Pod 从 Service 的 Endpoint 里移除,流量就不会再打过来了。

三者的区别,我总结了一张表:
检查类型 检查对象 失败后果 典型场景
心跳检测 节点/进程是否存在 标记为故障,触发自愈 边缘节点与中心通信
探活 进程内部是否健康 重启容器/进程 应用卡死、死锁
就绪检查 服务是否可接受流量 从负载均衡中移除 启动预热、临时过载

2.4 三种检查的协同工作

这三种检查不是互斥的,而是互补的。我习惯把它们组合起来用:

  1. 心跳检测:负责节点级别的存活,超时了就触发节点迁移或重启
  2. 探活:负责进程级别的健康,挂了就自动拉起
  3. 就绪检查:负责流量级别的可用,没准备好就不让进流量

举个例子:一个边缘节点突然网络闪断,心跳丢了两次,但很快恢复了。这时候心跳检测可能会标记「疑似」,但探活和就绪检查都正常,所以不会触发重启。如果心跳连续丢了5次,那就判定节点挂了,直接触发节点迁移。

避坑指南:我曾经把探活和就绪检查的阈值设得一模一样,结果节点一抖动就重启,一重启就抖动,形成了「抖动放大」的恶性循环。后来我把探活的 failureThreshold 设大一点(比如5次),就绪检查设小一点(比如2次),这样节点抖动时先摘流量,不急着重启,稳定多了。

2.5 实现时的几个关键参数

嗯,这里要注意几个参数,调不好会出大问题:

  • initialDelaySeconds:启动延迟。别设太短,应用还没启动完就开始检查,肯定失败。我一般设 10-30 秒,看应用启动速度。
  • periodSeconds:检查间隔。太频繁浪费资源,太慢反应迟钝。5-15 秒比较合理。
  • failureThreshold:失败阈值。边缘网络不稳定,建议设 3-5 次,避免网络抖动就误判。
  • successThreshold:成功阈值。就绪检查一般设 1 次,探活可以设 1-2 次。

下面这张图展示了三种检查在边缘节点上的协作流程:

边缘节点健康检查协作流程 边缘节点 心跳检测 每5秒发送一次 探活检查 检查进程内部状态 就绪检查 检查是否可接流量 中心控制器 心跳超时判定 触发节点迁移/重启 探活失败判定 触发容器重启 就绪失败判定 从负载均衡移除 负载均衡器 根据就绪状态 决定是否转发流量 心跳检测 探活检查 就绪检查 负载均衡

说白了,这三种检查机制就是边缘节点自愈的「三道防线」。心跳检测看节点在不在,探活看进程好不好,就绪检查看服务能不能用。把它们配合好,边缘节点的故障自愈就有了坚实的基础。