3. 健康检查入门:健康检查的目的、被动健康检查与主动健康检查的区别

你想想看,负载均衡器把流量分发到多台后端服务器,听起来很美好。但万一其中一台服务器已经挂了,或者正在半死不活地响应超时呢?

负载均衡器如果还傻乎乎地继续往那台机器发请求,那用户的体验就是——页面转圈、报错、甚至连接被重置。

嗯,这就是健康检查要解决的问题。

3.1 健康检查的底层逻辑

说白了,健康检查就是负载均衡器用来判断后端节点“是死是活”的一套探测机制。它不是锦上添花,而是系统的底线。

我个人习惯把健康检查的目的归纳为三点:

  • 踢掉坏节点——当后端服务异常时,立即将其移出转发池
  • 恢复后自动接入——节点恢复正常后,重新加入服务
  • 保证新节点可用——新加入的机器先检查再上线,不直接接流量

我在项目中遇到过好几次极端情况——比如某次线上压测,一台机器的CPU飙到95%,但TCP端口还活着。如果用简单的端口检查,这台机器仍然是“健康的”。但实际呢?请求进去全卡住。这就是为什么健康检查不能只看表面。

我曾经踩过一个坑: 有个网关层只配置了TCP端口检测,后端应用内存泄漏了三天都没被发现。因为进程还在、端口还开着,但每次请求处理耗时已经涨到8秒。从那以后,我对"健康"的定义就苛刻多了。

3.2 被动健康检查

被动健康检查,也叫“懒汉检查”。它不主动去探测后端,而是通过监控真实流量的表现来做判断。

举个例子——负载均衡器把请求发给后端A,结果返回了5xx状态码。被动检查就会记录这次失败。当失败次数达到一个阈值时,就把A标记为不健康,暂时踢出转发池。

它的核心机制就几个点:

  • 观测指标:响应状态码(4xx/5xx)、超时、连接失败、TLS握手失败
  • 统计窗口:比如最近30秒内的失败率
  • 淘汰阈值:连续N次失败,或失败率超过X%
  • 恢复策略:经过一段冷却时间后,重新尝试转发少量请求做“试探”

为什么叫“被动”?因为它不产生额外的探测流量。所有判断都基于用户请求的结果。这样做的好处很直观——不浪费带宽,不增加系统开销,尤其在加密服务场景下,主动探测也要走TLS握手,成本不低。所以很多团队在低配环境下优先选择被动检查。

但被动检查有个明显的短板:判断滞后。

假设后端在两次请求之间挂掉了。下一次用户请求正好打到这台机器上,才会触发探测失败。也就是说,那第一个用户就倒霉了。在高可用场景下,这通常是不可接受的。

3.3 主动健康检查

主动健康检查,是更“积极”的方式。负载均衡器会定时向后端服务器发送专门的探测请求,不管有没有用户流量,都一直测。

这种探测可以是:

  • ICMP Ping(网络层)
  • TCP连接测试(传输层)
  • TLS握手测试(针对加密服务)
  • HTTP/HTTPS请求应用层接口(例如 GET /health)
  • 自定义脚本或gRPC健康检查

主动检查的好处很明显——能在流量到达之前发现问题。后端刚启动好,还在预热?主动检查就知道它还没就绪,不会把流量分过去。后端突然挂了?下一轮探测(一般是1-5秒间隔)就能检测到,立刻踢掉。

在加密服务里,主动检查有个特殊场景。我建议每个HTTPS后端都暴露一个/health接口,并且这个接口的证书验证和业务完全一致。这样探测结果才能真正反映客户端眼看的世界。

# NGINX配置示例 —— 主动健康检查
upstream backend {
    zone backend 64k;
    
    server backend1.example.com:443;
    server backend2.example.com:443;
    
    # 主动健康检查
    health_check interval=5s fails=3 passes=2;
    
    # 指定检查路径与Host
    health_check uri=/health;
    health_check mandatory;
}

# 对应的后端health接口可以返回200和简单JSON
location /health {
    return 200 '{"status":"ok"}';
    add_header Content-Type application/json;
}

但主动检查不是免费的。每一次探测都消耗连接、可能还要做TLS握手,频繁了会加重后端负担。检查间隔太长,又失去了“提前发现”的意义。所以要在检测灵敏度和系统开销之间做取舍。

我的建议: 在加密服务负载均衡中,主动健康检查的间隔设为3-5秒,失败阈值设为2-3次。这样平均发现时间在6-15秒内,而且不会把后端打爆。

3.4 对比:被动 vs. 主动

这两种方式不是二选一。我参与过的生产项目中,绝大多数成熟方案都是两者并用的。它们互相弥补短板。

维度 被动健康检查 主动健康检查
探测方式 基于真实用户请求的响应 定时发送独立探测请求
额外开销 几乎为零 产生探测流量,TLS握手成本
发现故障速度 慢,取决于用户请求到达 快,可配置短间隔
适用场景 流量已满的集群、成本敏感环境 高可用要求、新节点上线、冷启动
检测深度 仅限请求层面(状态码、超时) 可到应用层(接口返回值验证)
漏检风险 如果节点无用户流量,故障不会被发现 探测请求与实际用户请求路径可能不一致

看到了吗?两个都有盲区。主动检查可能测到health接口正常,但实际业务接口已经坏了——比如数据库挂了,只有health接口没连数据库。被动检查能真实反映业务请求到底好不好用,但第一波用户已经受伤了。

3.5 组合策略才是王道

我个人习惯用主动检查做快速发现,用被动检查做有效性验证。

具体流程是这样的:

  1. 主动检查每3秒探测一次所有后端,一旦连续失败,立即标记为“不健康”并踢出
  2. 被踢出的后端仍然接受低频率的主动探测(比如每10秒一次)
  3. 恢复后后端重新加入转发池,但会先接收少量流量
  4. 被动检查持续监控真实请求的表现,如果发现刚恢复的节点再次大量5xx,会再次踢出

你看,主动检查负责“快”,被动检查负责“准”。

在HAProxy里,这种组合有直接的关键字支持:

# HAProxy配置 —— 主动+被动组合
backend servers
    balance roundrobin
    
    # 主动检查(每2秒一次,连续3次失败标记down)
    option httpchk GET /health HTTP/1.1\r\nHost:\ example.com
    default-server inter 2s fall 3 rise 2
    
    server srv1 10.0.0.1:443 check inter 2s fall 3 rise 2 weight 100
    server srv2 10.0.0.2:443 check inter 2s fall 3 rise 2 weight 100
    
    # 被动检查(连续10次5xx则剔除此后端1分钟)
    option redispatch
    retries 3
    timeout connect 5s

为什么强调组合?因为我见过太多团队只用一个。只开被动检查的,半夜流量低谷时后端挂了,到了早上才发现。只开主动检查的,health接口一切正常,实际业务早崩了。

核心总结:

  • 健康检查是负载均衡器的“眼睛”,让你知道后端节点到底行不行
  • 被动检查省资源但反应慢,适合附加监控
  • 主动检查响应快但开销大,是主防线
  • 组合使用 = 主动做快速发现 + 被动做真实验证
  • 加密服务场景中,主动检查的TLS握手要与真实客户端一致

好,理解了健康检查是什么以及两种方式的区别,下一步我们就可以进入怎么配置、怎么选参数、怎么避免常见的坑了。