bigywater.tech
微服务 · 45

健康检查:主动探测与实例上报,探测间隔与摘除延迟

主动探测和实例上报心跳,哪个更早发现故障?进程没死但处理不了请求时,心跳还有意义吗?探测的参数和线程池大小怎么影响摘除延迟?这篇用本机的小服务量出来。

0 / 5
业务线程池卡死(hang)、心跳线程仍在发时,心跳 + TTL 机制摘除该实例的次数;探测机制是 5 / 5(2.24 s)
2.04 到 3.26 s
探测间隔 1 s、连续失败 3 次摘除时,crash 与 hang 的摘除延迟范围
2.07 s 对 1.07 s
200 个实例里 20 个 hang、探测超时 1 s 时,线程池 10 与 50 的一轮探测用时

探测的参数

实测 · 本机执行输出
  探测间隔 · 失败阈值       | crash:摘除延迟 最短 / 中位数 / 最长 | hang:摘除延迟 最短 / 中位数 / 最长
  ------------------+--------------------------+------------------------
  I = 0.5 s · K = 1 | 0.00 / 0.17 / 0.48 s     | 0.40 / 0.59 / 0.80 s   
  I = 0.5 s · K = 3 | 1.01 / 1.25 / 1.45 s     | 1.33 / 1.54 / 1.75 s   
  I = 1.0 s · K = 1 | 0.01 / 0.18 / 0.78 s     | 0.32 / 0.84 / 1.30 s   
  I = 1.0 s · K = 3 | 2.04 / 2.67 / 2.95 s     | 2.48 / 2.81 / 3.26 s   
  摘除延迟大致在 (K−1)×I 到 K×I 加上探测超时之间:crash 时连接立刻被拒,每次探测立刻失败;hang 时每次探测要等满 0.3 s 的超时才算失败,所以多出一个超时

摘除延迟在 (K−1)×I 到 K×I 之间;hang 时每次探测要等满超时。

心跳的盲区

实测 · 本机执行输出
  故障    | 心跳 + TTL 1.5 s:摘除延迟(中位数) | 探测 I = 0.5 s · K = 3:摘除延迟(中位数) | 故障之后发给它的 /work 请求成功数
  ------+--------------------------+--------------------------------+---------------------
  crash | 1.32 s(5 / 5 次摘除)        | 1.33 s(5 / 5 次摘除)              | 0 / 50              
  hang  | 一直认为健康(0 / 5 次摘除)        | 2.24 s(5 / 5 次摘除)              | 0 / 50              
  hang 时心跳线程和业务线程是独立的,心跳继续上报“我还活着”,注册中心不会摘掉它,发给它的请求全部超时;探测方因为 /health 也走业务线程池,探测会失败,所以能摘掉。心跳只能证明“进程活着、心跳线程活着”,不能证明“能处理请求”;要让心跳有意义,上报之前应当做一次真实的自检(走和业务一样的路径)

心跳只能证明进程和心跳线程还活着;业务线程卡死时心跳照常发,注册中心不会摘除。

探测的开销

实测 · 本机执行输出
  线程池大小 | 一轮探测 200 个实例用时(中位数) | 3 轮范围        
  ------+---------------------+--------------
  10    | 2.07 s              | 2.06 ~ 2.07 s
  50    | 1.08 s              | 1.06 ~ 1.08 s
  200   | 1.07 s              | 1.07 ~ 1.08 s
  理论下界:20 个 hang 的实例各占一个线程 1 s,用 10 个线程就至少要 2 s 才能处理完它们;线程池太小时,少量 hang 的实例就会把整轮探测拖慢,其它健康实例的探测间隔也跟着变长——探测间隔设成 1 s,实际可能是几秒

探测线程池太小时,少量 hang 的实例就拖慢整轮探测。

分步演示

健康检查
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。实例是本进程里的 Python 服务(业务请求和 /health 都经过同一个线程池;hang = 处理请求时 sleep 30 s;另有一个独立的心跳线程);探测方与心跳的“注册中心”是我写的几十行 Python(没有真正的注册中心);第一节每个组合 15 次,第二节每种故障 5 次,第三节 3 轮
    • 没有验证:Consul / Nacos 自带的健康检查类型(见后面的篇目);探测方与实例之间的网络分区(探测失败但客户端能访问的误摘除);心跳走网络时丢包的影响(见第 40 篇);深度健康检查(检查依赖的数据库)带来的连锁摘除
    京ICP备2026067502号-1京公网安备11010502063487号