在 Apache HTTP Server 作为反向代理的架构中,后端应用服务器的健康状态直接影响前端可用性。mod_proxy 模块提供了两种健康检查机制:主动探测和被动观察。被动健康检查不需要定时发送探测请求,而是根据实际转发请求的结果来评估后端是否健康,因此对应用性能的干扰更小,也更容易部署。

被动健康检查的核心在于:当 Apache 向后端节点转发请求时,如果遇到连接超时、连接被拒绝或者后端返回特定错误状态码,Apache 会将该节点标记为失败状态。标记失败后,Apache 会在一定时间内不再向该节点发送新的请求,这段时间叫做重试间隔。重试间隔结束后,Apache 会尝试重新将请求发送给该节点,如果成功,则恢复其健康状态;如果再次失败,则重新进入冷却期。整个过程完全基于真实业务流量触发,不需要额外的监控脚本。
需要注意的是,被动健康检查的“被动”并不意味着完全不用配置。很多开发者拿到默认配置后发现后端节点宕机后Apache 依然会继续转发请求,导致大量错误日志。其根本原因在于 Apache 默认并不会因为单次请求失败就永久剔除后端,而是需要经过 retry 参数设定的时间窗口,并且配合 failonstatus、failontimeout 等条件判断。下文会逐个拆解这些参数。
mod_proxy 被动健康检查的触发条件
mod_proxy 在转发请求时,会维护一个内部的工作线程状态表。对于每一个后端工作节点(worker),它记录了该节点当前是否可用,以及不可用的截止时间。被动健康检查的触发条件来自几个关键指令:failonstatus、failontimeout 和 failonconnectionerror。其中 failonstatus 用于指定哪些 HTTP 状态码算作请求失败,例如后端返回 500、502、503 时,Apache 可以认为该节点出现问题。而 failontimeout 则针对连接超时或读取超时的情况。
当一次请求满足上述失败条件时,Apache 并不会立刻将该 worker 标记为完全不可用,而是标记为“错误”状态。此时该 worker 仍然可以继续接收新的请求,直到累积的错误次数达到 max_attempts 或者冷却期结束。这里有一个容易混淆的概念:被动检查中的“失败”并非永久剔除,而是一个基于时间的冷却。默认情况下,max_attempts 没有设置,意味着只要失败一次就会进入冷却期,但冷却期长度由 retry 参数控制,默认值为 60 秒。也就是说,默认配置下后端节点失败一次后,60 秒内不会被再次使用,60 秒后自动恢复尝试。
这种设计可以避免因为偶发的网络抖动就把后端节点长期下架,但同时也带来一个问题:如果后端确实宕机了,Apache 只会每 60 秒尝试一次,期间所有请求都会返回 503 错误给客户端。对于高并发生产环境,这种等待时间可能过长。合理的做法是根据业务容忍度调整 retry 的值,同时结合 max_attempts 控制失败尝试次数。
关键参数详解与配置示例
Apache mod_proxy 中和被动健康检查及重试相关的指令主要集中在 <Proxy> 上下文中。常用的参数如下:
- retry:指定 worker 因错误被标记后,多长时间(秒)内不再使用该 worker。默认 60 秒。如果设置为 0,表示不重试,即一旦失败永久不再使用该节点,直到 Apache 重载或重启。
- failonstatus:指定后端响应哪些状态码时触发失败逻辑。例如
failonstatus=500,502,503。 - failontimeout:指定当连接超时时是否触发失败逻辑。可选值为
on或off。 - failonconnectionerror:指定连接错误(如连接被拒绝)是否触发失败逻辑,同样可选 on 或 off。
- max_attempts:在 worker 被标记为失败之前,允许失败的最大次数。注意这个参数需要结合
retry使用,因为一旦达到最大尝试次数,worker 会被标记为失败并进入冷却期。
下面是一段完整的 Apache httpd.conf 配置示例,展示如何启用被动健康检查并配置合理的重试策略。
# 加载必要的模块
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
# 定义反向代理后端集群
<Proxy balancer://mycluster>
BalancerMember http://192.168.1.10:8080 retry=15
BalancerMember http://192.168.1.11:8080 retry=15
ProxySet lbmethod=byrequests
ProxySet failonstatus=500,502,503
ProxySet failontimeout=on
ProxySet failonconnectionerror=on
ProxySet max_attempts=3
</Proxy>
# 将根路径转发到负载均衡集群
ProxyPass / balancer://mycluster/
ProxyPassReverse / balancer://mycluster/
在上面的配置中,每个 BalancerMember 都指定了 retry=15,这意味着如果某个后端节点因为满足失败条件而被标记,Apache 会在 15 秒内不再向该节点发送请求。同时 max_attempts=3 表示在触发冷却期之前,同一个节点最多允许连续失败 3 次。如果三次尝试都失败(每次间隔由重试时间决定),则该节点进入 15 秒冷却。冷却结束后,Apache 会再次尝试转发,若成功则恢复正常。
需要特别说明的是,max_attempts 的作用方式和很多人的直觉不同。它不是指“失败后重试几次”,而是指“在进入冷却期之前允许的失败次数”。达到这个次数后 worker 才会被完全下架,且下架时间由 retry 决定。如果 max_attempts 未设置,默认任何一次失败都会直接进入冷却期,这其实更符合被动检查的常规理解。但如果设置了 max_attempts,则会允许多次失败后才冷却。
故障场景模拟与调优建议
为了验证被动健康检查和重试是否真的生效,可以在测试环境中手动停掉其中一个后端节点。例如先启动 Apache 并配置两个 BalancerMember,然后使用 curl 连续发送请求。当其中一个节点被主动终止进程后,Apache 会在下一次请求转发时遇到连接被拒绝或超时,触发失败条件。接下来观察 Apache 的错误日志(通常为 error_log),可以看到类似 “proxy: BalancerMember worker ... failed” 的记录。
在故障转移过程中,客户端可能会出现一次 503 错误,因为 Apache 需要先确认失败后才能将请求转发到健康的节点。当多个后端节点同时存在时,Apache 会跳过已冷却的节点。但要注意一个边缘情况:如果所有节点都因为被动检查进入冷却期,Apache 将返回 503 Service Unavailable,直到至少一个节点冷却结束并恢复。因此不建议将所有节点的 retry 设置得过大,否则整个集群可能在故障期间长时间不可用。
调优时还有几个常见误区。第一,retry 参数是加在 BalancerMember 行内还是 ProxySet 中?正确答案是前者,因为 retry 是 worker 级别的属性,不能通过 ProxySet 全局设置。第二,failonstatus 中的状态码列表不能包含空格,多个状态码用逗号分隔。第三,被动健康检查只针对转发过程中出现的错误,对于后端服务正常响应但业务逻辑错误的情况无能为力。如果需要监控业务健康状态,必须搭配主动健康检查或外部探活组件。
此外,如果后端应用经常出现瞬时高延迟,可以适当放大 timeout(通过 ProxyPass 的 timeout 参数或全局 Timeout),避免因慢请求误判节点失败。例如后端为 Java 应用且 GC 停顿可能超过 5 秒时,默认的 Apache 代理超时 60 秒虽然足够,但如果反向代理还处理文件上传等长连接,建议根据实际情况调整。被动检查的触发条件越精确,误判概率就越低,重试机制才能更准确地发挥作用。
最后强调一点,被动健康检查的重试并不意味着 Apache 会主动重新发送已经失败的请求。对于已经返回给客户端的错误响应,Apache 不会自动进行请求重放。这里的“重试”指的是恢复后再使用该节点的能力,而不是请求级别的重试。如果需要在请求级别实现自动重试,需要引入其他模块或在上层应用处理。