摘要

  • RFC 2391 让一个虚拟服务器地址代表一个后端池;LSNAT 为每条新会话选择成员,并以绑定状态约束后续所有数据包。
  • 轮询、会话数、流量、权重、路由代价与探活都只是选择依据,不能证明应用真实容量,更不能证明请求一定成功。
  • 把死机从新分配名单中移除,只保护未来会话;已经绑定的会话不能中途换机,避错、连续性与故障接管是三张不同凭证。

地址没有告诉客户端:谁在接单

服务器扩容最容易被讲成一道除法题:一台机器扛不住,就增加几台,把请求平均分开。真正困难的不是池里有几台机器,而是客户端仍然只认识一个服务入口,同时每一条持续中的对话必须找到同一个执行者。

RFC 2391 把 RFC 1631 的网络地址翻译,与“实时”负载分配算法组合在一起。客户端继续访问虚拟服务器地址。LSNAT 在中间看到新会话,从池里挑出一台机器,改写数据包并转发。池成员可以添加、替换或下线,客户端无需随之修改。

这种透明性没有消灭决定,只是把决定挪走了。虚拟地址不再等于某台物理服务器的身份,它指向一个选择面。谁有资格进入池、哪些服务参与分配、采用哪项指标、哪个成员被选中,都发生在客户端看不到的本地控制面上。

这与 RFC 1546 的任播历史不一样。任播关注同一个服务地址如何经由路由抵达不同实例,以及逐数据报选择会怎样破坏稳定身份。RFC 2391 的特征是一个记住选择的翻译器。它不是每次问“这个包去哪”,而是在第一次选择之后问“这条会话已经归谁,后续包怎样继续服从这个记录”。

第一次选择为什么会变成约束

RFC 2391 把翻译过程分为三段。第一段是会话绑定:新会话与池中某台主机的地址建立关联,这项关联给后续所有数据报设定翻译参数。第二段是查找与翻译:属于同一连接的包先查会话表,再改写相关字段。第三段是解绑:系统判断会话已经结束,服务器不再对它负责。

“绑定”不是一个推荐结果,而是一段有权力的状态。去程数据包的目的地址、必要时还有目的端口,被改成后端的值;回程包的源地址和端口被改回客户端认识的虚拟服务。IP、TCP、UDP 或 ICMP 校验字段也要随之调整。

因此,请求与响应必须穿过同一个 LSNAT。若回程绕开翻译器,客户端会看到不匹配的身份。若故障后流量被送到另一台没有会话表的翻译器,它只知道虚拟地址,不知道当初选了哪台后端,也不知道端口怎样对应。

RFC 2391 对此没有模糊处理:会话一旦分配给某台主机,在结束以前不能移到另一台。分配算法只决定新会话的归属,不负责搬运传输层状态、应用上下文或已经做到一半的工作。

“负载”其实有许多互不相同的替身

最简单的轮询不观察负载。它只保证选择次序均匀,暗中假设每条会话成本相近、每台服务器能力相近。一次昂贵查询和一次廉价读取,在轮询表里都是“一个”。

“最少会话优先”多看了一层:谁当前绑定的会话少,下一条就交给谁。它仍然把长时间空闲连接与高速传输算成同一种单位,也把不同硬件当成同等资源。

按数据包或字节计数,可以观察到翻译器真正经过的流量,却仍然只是在近似系统负载。一个很小的请求可能触发昂贵计算;大块顺序传输反而可能只消耗少量 CPU。可见的数据面指标与后端内部压力并不是同一事实。

权重把运维判断写进公式。FTP 会话可以比 Telnet 会话权重高,一台更强的服务器可以获得更高能力系数。这让假设变得可调、可审计,却没有让它变成实时测量。数字越精细,也可能只是更精细地表达旧经验。

RFC 2391 直言,想精确知道远端主机当下有多少未使用容量并不容易。后端可以主动向选择器报告资源,但这又引入报告周期、延迟、真实性和过期问题。状态一旦跨越网络,就已经可能落后于下一次选择。

这些算法真正能回答的是:“在当前模型和当前证据下,下一条会话交给谁?”它们不能回答“谁拥有客观最多的容量”,也不能替某个用户保证结果。

路由代价只说明选择器所见的路径

当服务器分布在不同位置,RFC 2391 允许选择器读取 RIP、OSPF 等路由信息,用到达代价参与决策。算法可以把路径代价与已分配会话数或流量结合。网络故障令某台服务器不可达时,它的代价可设为无穷大,从而不再接收新会话。

这项信号有明确边界。路由表说明控制面是否知道一条路径以及如何评价它,并没有预留带宽、CPU、内存或应用队列。路径存在,也不等于服务进程健康;路径短,也不等于请求会完成。

这正好与 RFC 2386 的资源地图文章划开边界。那篇讨论地图为何不是准入、预留或交付;RFC 2391 在地图或负载指标之后又多了一步——它把局部判断写成会话绑定。两者都提醒我们:描述某种可能性,并不会自动生成下一层现实。

完整证据链应当是:虚拟地址、池成员配置、负载或路径信号、选择策略、会话绑定、双向翻译、服务器响应、应用结果。少掉其中任何一环,绿色状态都可能只是提前颁发的结论。

宣布“死机”能改变未来,不能改写过去

若选择器继续向不响应的服务器分配新会话,负载均衡就会变成黑洞制造器。RFC 2391 因而提出启发式探活:定期发送 ping,或者在刚分配新会话后观察该成员是否发回数据报。数秒内没有响应,可以宣布它死亡,并停止向它分配新会话。

重新纳入也依赖试探。等待几分钟后,系统可以周期性地再次分配新会话,观察响应时间。所谓“存活”不是永久登记,而是某项本地策略依据有限信号得出的暂时结论。

ping 通只证明某一层能回声,不证明应用及其数据库、磁盘或证书链可用。新会话无响应也未必准确指出故障位于主机、网络、服务还是请求本身。选择器必须在原因尚未完全闭合时采取行动。

这里有一个必须标明为推论、但由原文两条事实共同支持的结论。探活章节说的是停止分配“新会话”;前文又说已分配会话在结束前不能迁移。因此,死机抑制可以减少下一次错误选择,却不能把现有会话接到健康后端。

“池整体仍然可用”和“这位用户的会话仍然连续”会同时一真一假。三台健康服务器可以继续接单,第四台留下的所有绑定却仍然失败。用池级可用率覆盖会话级损失,会把恰恰需要观察的对象抹掉。

RFC 3022 后来把同一问题延伸到 NAT 自身:故障后把包改道到另一台 NAT,可能在切换时令流失败;除非设备共享配置与会话状态,新的路径并不能还原旧对话。RFC 3234 进一步区分故障接管与重启:硬状态若没有副本,替代机器只能让用户重新开始。

系统也不总知道会话何时真正结束

绑定不能永久保留,所以 LSNAT 还要决定何时解绑。TCP 的 FIN、RST 提供明显线索,但对端重启、包丢失或异常消失,可能让翻译器收不到终止信号。UDP 没有统一的“会话结束”消息。应用认为的一段业务,也未必与 NAT 看到的五元组生命周期相同。

空闲超时于是承担了判断。时间太短,会删除仍在等待下一次数据的合法绑定;时间太长,会积累已经死亡的状态并占用表项与端口。RFC 2663 后来明确指出:一般情况下无法区分长时间空闲与已经消失的连接,NAT 的会话观也不保证等于应用的会话观。

RFC 4787 又为 UDP 映射给出最低超时与刷新要求,同时记录不同实现间的巨大差异。这类规则改善兼容性,但仍不能把沉默变成确定知识。

解绑不是后台清理小事。它决定旧身份何时停止有效、迟到的数据包是否还能找到责任人,以及有限资源何时可以复用。会话的诞生与死亡,都由中间点在不完整证据上作出本地判断。

LS-NAPT 用更多状态换取更少拓扑束缚

基础方案把后端池放在一个所有往返都自然经过 LSNAT 的边界内。RFC 2391 还描述了 LS-NAPT:通过同时改写两侧字段,迫使客户端与服务器都把数据包送回翻译器。这样,后端不必局限在同一个存根域,接入链路也更容易扩展。

代价同样写在规范里。需要更多翻译,处理更复杂;该配置只面向 TCP 与 UDP;可用客户端端口限制同时会话规模。物理拓扑限制被放宽,依赖却转移到端口空间、状态表和中间点性能。

这延续了 NAT 的根本交换。RFC 1631 认为无需修改主机即可增量部署是巨大优势,同时承认 IP 地址的端到端意义被拿走,网络内部必须增加状态。LSNAT 利用这种可塑性,把服务器选择也塞进了地址背后。

RFC 7098 后来把“持久性”定义为让一条客户端会话在同一台服务器上完成,并描述有状态负载均衡器保存“流标识—服务器”关联。这个词能准确解释 RFC 2391,却也容易被误读:持久性意味着不换,并不意味着随时能换。

一张虚拟地址,八层不同现实

虚拟地址只证明入口。池配置只证明候选名单。测量只证明选择器看见了某种信号。算法只证明按某条规则做了选择。绑定证明某条会话归属某成员。翻译证明数据包按该记录通过。服务器响应证明部分运行行为。应用结果才证明用户要的工作是否完成。

每层都有自己的控制者。运维者管理池和策略;选择器解释指标;LSNAT 保存权威绑定;后端掌握实际资源和应用状态;端点观察最终效果。任何一层越权替后面几层签字,都会制造虚假确定性。

Heng Lu 的笔记反复强调现实层级:初始规范应尽量小,未来决定留在本地,记录描述现实而不创造现实,最终由运行代码与观察结果交付证据。RFC 2391 正好展示了这一纪律。它让客户端界面变得简单,却没有否认简单界面背后的选择、状态、路径与不确定性。

池里确实还有空闲机器。问题是那条会话已经不是“下一条”。一旦选择写进状态,剩余容量便不再是一份故障接管方案。

来源