后端 A 被摘除后,为什么新请求去了 B,一条已经连到 A 的长连接却还能继续得到 A 的响应?“后端健康”若没有同时说明选择单位、检查时刻和已有状态,只是一个容易误导的标签。

第 21–24 篇分别给出了名字解析、HTTP 与 TLS,第 31 篇解释五元组和有状态转发。本篇把入口之后的选择拆成四层负载均衡、七层反向代理和健康判定,并用本机连接实验观察后端关闭监听前后的差别。

四层选择的是连接目标

四层负载均衡依据 IP、端口和传输协议等信息选择后端。实现可以转发报文、改写地址,也可以终止客户端连接后再建立上游连接;“四层”描述决策主要不依赖 HTTP 路径、方法或字段,不保证实现内部一定只有一条连接。

一次 TCP 连接选中后端 A 后,后续字节通常沿已有映射或上游连接继续发送。轮询算法按连接选择时,一个包含许多请求的长连接只贡献一次选择;因此“连接数平均”不等于“请求数平均”,更不等于后端工作量平均。

关闭 A 的监听 socket 只阻止新连接进入。操作系统中已经建立的连接是独立对象,只要进程继续服务并且连接未被关闭,已有客户端仍可交换数据。若进程退出、连接被重置或负载均衡器主动排空,结果才会不同。

模式提炼:先确定调度单位

负载分布 = 选择算法 × 调度单位 × 状态保持时间

轮询、最少连接或哈希只有绑定到“连接、请求还是流”后才有完整含义。遇到负载不均,首先检查长连接、连接池和复用,而不是只看算法名称。

七层代理能按请求语义选择

RFC 9110 把 proxy、gateway 和 tunnel 都列为 HTTP intermediary。反向代理通常接收客户端 HTTP 请求,解析目标、路径和字段,再向选定上游发送请求并把响应返回客户端。NGINX 的 proxy_pass 可以把请求交给单个服务器或上游组,也能改写发往上游的请求字段。

七层代理因而可以按 host、路径、方法、header 或 cookie 分流,也能执行重试、缓冲和协议转换。代价是它必须理解相应应用协议,并分别管理下游与上游连接。客户端的一条 HTTP/2 或 HTTP/3 连接可能承载许多请求,代理可以让不同请求去不同后端;这与四层按 TCP/QUIC 流固定后端不是同一粒度。

TLS 终止位置决定代理能看到什么。若入口只转发加密字节,通常不能读取加密后的 HTTP 路径;若在入口终止 TLS,再向上游建立另一条连接,它才能按 HTTP 语义路由,同时承担证书、身份、header 信任和上游加密策略。

健康检查是带延迟的分类

主动健康检查由负载均衡器周期性连接后端,或发送 HTTP/gRPC 请求,再按成功、失败阈值更新状态。Envoy 文档明确区分主动检查和基于真实请求结果的被动异常检测,并允许配置检查间隔以及判定健康/不健康所需次数。

健康结果只证明探针在某个时间、从某个位置、用某种协议得到结果。TCP 建连成功不能证明业务依赖可用;HTTP 200 也可能只证明健康端点自身正常。反过来,一次超时可能来自探针路径或瞬时过载,未必代表所有客户端都无法访问。

健康状态变化和流量变化之间存在窗口:

  • 故障发生到探针发现之间,新连接仍可能选到 A;
  • A 被标记不健康后,新选择通常排除 A;
  • 已有连接是否保留、排空或中断,由产品配置和失败类型决定;
  • A 恢复后,还可能需要连续成功或慢启动才能重新接流量。

因此,控制面的后端集合、健康事件和数据面的连接/请求日志必须对齐时间。只看最终“healthy=false”无法解释故障窗口内每个请求。

本机连接实验

同名素材目录中的 lab.py 启动后端 A、后端 B 和一个只绑定 127.0.0.1 的教学四层代理。代理在接受新客户端连接时,从当前健康后端中选择一个,然后保持到连接关闭。实验依次执行:

  1. 建立长连接,确认它选中 A;
  2. 关闭 A 的监听 socket,但保留已建立的处理连接;
  3. 重新探测 A/B,得到 A 不可新建连接、B 可连接;
  4. 长连接再次发送请求,仍收到 A 的响应;
  5. 新连接经代理选择 B。

运行命令:

1
2
python3 source/_posts/2026-09-24-计算机网络32-请求到达哪个后端/lab.py --help
python3 source/_posts/2026-09-24-计算机网络32-请求到达哪个后端/lab.py

素材包括回环代理实验、本次输出、资料与环境记录和审阅记录。

脚本只验证按连接选择和关闭监听后的局部行为。它不是通用负载均衡器,没有实现双向任意字节转发、超时重试、连接排空、TLS 或 HTTP 路由,运行结果也不是性能数据。

条件、限制和反例

“后端不健康会立即停止所有流量”不成立。探测有间隔和阈值,已有连接还可能继续。若要求立即断开,需要单独定义连接终止或排空策略,并评估正在处理的请求。

“轮询必然均衡”忽略了请求成本和连接复用。两个连接分别处理一个请求和一万个请求时,连接数相等但负载悬殊。七层按请求轮询也可能被慢请求、流式响应或会话亲和打破。

“代理返回 502 就是后端宕机”也过于绝对。RFC 9110 中 502 表示 gateway/proxy 从上游收到无效响应;连接失败、协议错误、上游提前关闭和代理配置都可能触发相近表象。需要上游连接日志和后端证据继续区分。

练习

练习一:四层入口有两个后端,客户端使用一条持续十分钟的连接发送一万次请求。解释为什么轮询连接不能保证请求均分,并给出两种改变调度粒度的办法。

练习二:健康探针每 5 秒执行一次,连续三次失败才摘除后端。列出故障发生后新连接仍被送往该后端的最长判定窗口所依赖的条件。说明为什么不能只凭“最终已摘除”推断窗口内请求结果。

模式速查表

需求或现象 优先核对 容易混淆
长连接流量不均 调度单位与连接复用 连接数等于请求数
按路径分后端 TLS 终止与七层解析 四层设备能读取密文路径
后端刚坏仍收到新流量 探测间隔、阈值与传播 健康状态瞬时一致
摘除后旧请求仍在 已有连接与排空策略 摘除等于断开
502/504 代理到上游的阶段证据 状态码唯一指向宕机

官方参考资料

验证边界

已验证:Python 3 标准库在 loopback 上运行两个自建后端与按连接选择的教学代理,关闭 A 的监听后,既有连接仍由 A 响应,新连接选择健康的 B。验证类型为 LOCAL_INTEGRATION。

NOT_RUN:未运行 NGINX、HAProxy、Envoy、TLS、HTTP/2/3、真实七层路由、跨主机健康检查、连接排空、故障重试、压力或生产流量。HTTP 200 与 Hexo 构建均不作为真实服务链证据。