S36

负载均衡

点击正文段落选定收藏或划线位置;未选定时使用屏幕内第一段。

课内目录

1 负载均衡要覆盖的流量面

值班时看到某个进程的 CPU 接近满载,旁边的主机却很空闲,直觉上会想把连接分过去。但客户端已经记住的地址、正在传输的文件和后端本地保存的会话,都不会因为增加进程而自动迁移。负载均衡器,简称 LB,提供稳定的访问入口,并依据规则把流量交给符合条件的后端。它解决的是流量如何分配的问题;扩容提供额外资源,健康检查提供筛选依据,应用自身仍要负责正确处理请求。

先明确分配对象。面向 TCP 服务时,常见对象是连接:连接建立后,后续字节通常继续交给同个后端。面向 HTTP 服务时,对象可能是请求:客户端的持久连接进入代理后,不同请求可以使用不同的后端连接。下载文件占用连接很久,查询状态可能很快结束,不能把“连接数相同”直接理解为“工作量相同”。业务入口、内部服务调用、文件访问以及管理接口,也未必适用相同规则。

流量还包括返回方向。请求经过 LB,并不必然表示响应以相同方式返回;具体路径取决于部署模式。入门设计应先选定能够解释清楚的代理路径,再核对地址、端口、路由和防火墙。否则只放通客户端到入口,忘记入口到后端,或者忘记回程,都会出现“端口能连上,文件却下载不完整”的现象。这里关注可观察的路径,不展开内核转发实现。

入口、后端与返回流量的范围
入口、后端与返回流量的范围

稳定入口本身也占用资源。LB 的进程需要 CPU 和内存,需要文件描述符接收连接,还需要足够的网络带宽。后端从 2 个增加到 4 个,若入口已经耗尽带宽,总吞吐量仍可能不变。规划时要分别记录入口容量、后端容量和共同依赖容量,例如所有后端都写入同个文件系统时,新增后端可能只增加等待写入的进程。

本课采用实验场景:本机入口监听 127.0.0.1:8080,后端 A、B 分别监听 127.0.0.1:9001 和 127.0.0.1:9002。后端提供状态页和静态文件,访问日志能够记录后端标识。实验中的入口可以是任意支持 HTTP 反向代理的实现,命令用于观察约定好的接口,不假定某款软件已经安装。部署到真实网络时,再把回环地址替换为经过确认的服务地址。

2 L4 与 L7 转发的能力边界

4层负载均衡主要依据传输层信息处理流量,常见依据包括来源地址、来源端口、目的地址、目的端口及协议。对于 TCP,通常在连接建立时选定后端,此后保持连接归属。它不需要理解 HTTP 路径,适用于多种基于 TCP 的服务。但“支持 TCP”不能推出“能够按下载目录分流”,因为目录名称存在于应用请求中,超出了这种分配方式通常使用的信息范围。

7层负载均衡理解特定应用协议。以 HTTP 反向代理为例,它可以依据主机名或路径选择后端池,把 /files/ 交给文件服务,把 /status/ 交给状态服务。它也能记录请求状态码、请求耗时和后端耗时,更容易区分入口拒绝、连接后端失败与后端返回错误。代价是需要解析协议、维护代理连接,并正确处理请求体、响应体和超时。

HTTPS 增加了观察边界。如果 TLS 在入口终止,入口能够看到解密后的 HTTP 内容,并可再次通过 TLS 连接后端。此时要管理入口证书,也要明确后端证书验证策略。如果入口仅透传加密连接,就不能假定它能够按照加密的 URL 路径分流。不要把某些握手信息可见,误写成整个请求内容都可见;选型要逐项确认所需规则是否有可用依据。

HTTP 代理路径中的连接与请求边界
HTTP 代理路径中的连接与请求边界

代理还会改变后端观察到的对端。后端的网络连接可能来自 LB,而非原始客户端。需要原始来源地址时,应使用双方明确支持的传递方式,并限定可信代理范围。对于 HTTP,某些代理通过约定的头字段传递来源信息,但公网客户端也能构造同名字段。后端若无条件信任它,就可能把伪造内容当成审计或访问控制依据。

选择层次时,先列出需求再作判断:只需让 TCP 连接分摊,还是要按 HTTP 路径分流;是否需要在入口处理证书;是否必须观测每次请求的状态。4层与7层不是能力高低的排名,而是观察信息和承担职责不同。还要核对实际实现的协议支持范围;支持 HTTP 代理并不自动等于支持所有升级连接、流式传输或 UDP 服务。

3 健康检查、会话保持与后端摘除

健康检查把“这个地址曾经可用”变成持续更新的判断。TCP 检查通常尝试建立连接,它能发现端口未监听、连接被拒绝或网络超时,却无法证明进程能读取文件。HTTP 检查可以访问 /ready 并验证状态码或约定内容,能够覆盖更多应用条件。检查接口应表达后端是否适合接收新流量,同时保持开销可控,避免每次检查都执行昂贵的目录扫描。

检查结果会滞后。假设间隔为 2 秒,连续失败 3 次才摘除,那么故障发生后通常还需要数秒才能确认;检查超时和执行安排也会影响总时间。恢复同样可以要求连续成功,避免后端在短暂恢复时立刻承接大量流量。失败阈值低,发现可能更快,但短暂抖动更容易触发摘除。阈值高,则会延长向故障后端分配新流量的时间。

摘除需要说明作用范围。停止分配新连接,不等于关闭已有连接;停止分配新请求,也不一定中断已经开始的响应。计划维护可以先停止接收新流量,再等待连接排空,超过维护窗口后按约定处理剩余连接。大文件下载、长时间状态流与短查询的排空时间不同。运维记录应写明最大等待时间以及到期动作,而不是只写“优雅下线”。

会话保持让某类客户端继续命中相同后端,常见依据是来源地址或 Cookie。它能暂时适配保存本地状态的程序,但会降低调度自由度。多个用户经由同个出口访问时,按来源地址保持可能把大量连接压到单个后端。Cookie 保持也要考虑后端失效后的规则:重新选择后端可以恢复访问,却未必找得到原来保存在本地的会话数据。

因此要分别定义存活、就绪和用户可用性。进程还活着,可能已经无法打开目标文件;就绪接口成功,可能只覆盖部分依赖;用户完整下载成功,才证明这次下载路径完成了任务。健康检查是调度输入,不能承担所有正确性验证。状态设计应配合真实请求观测,并明确故障期间现有连接、新请求和会话分别会发生什么。

4 动手:画一条请求经过 LB 的路径

先在纸上写下路径:客户端 shell 中的 curl,进入 8080,入口选择后端,再连接 9001 或 9002,后端进程打开文件并返回响应,入口把响应交还客户端。给每条连接标注发起方和监听方。这个动作能避免把入口端口、后端端口和客户端临时端口混在一起,也能帮助区分“入口没有接收连接”和“入口连接不到后端”。

在已经启动实验服务的 Linux 主机上,可用下面的命令查看监听情况。ss 的结果只反映执行命令所在网络环境;容器内外的观察位置不同,结果也可能不同。查看进程信息可能需要相应权限,不能因为进程名称缺失就断定没有监听。

ss -lntp 'sport = :8080'
ss -lntp 'sport = :9001'
ss -lntp 'sport = :9002'
curl --max-time 3 -i http://127.0.0.1:9001/ready
curl --max-time 3 -i http://127.0.0.1:9002/ready
curl --max-time 5 -i http://127.0.0.1:8080/status

直接访问后端是在隔离问题范围,不是在替代入口测试。后端成功而入口失败,下一步检查入口规则、连接后端的地址和权限;后端直接访问也失败,则先查后端监听、路径与进程日志。状态页应返回可区分的后端标识,例如 X-Backend: A,但不要输出内部凭据。真实环境可以把标识保留在受控日志中,避免向外暴露拓扑。

接下来连续发起少量请求,同时记录状态码和总耗时。下面的循环创建多个独立的 curl 进程,适合观察基础分配,不代表浏览器持久连接或生产并发。即使配置轮询,也不要要求每次输出严格呈现 A、B 交替,因为健康状态、并发和实现细节都可能影响顺序。

for n in 1 2 3 4 5 6; do
  curl --max-time 5 -sS -o /dev/null -D - \
    -w 'code=%{http_code} total=%{time_total}\n' \
    http://127.0.0.1:8080/status
done

文件访问要追加验证内容。将小型实验文件分别通过后端和入口下载到不同临时路径,用 sha256sum 比较,再检查文件大小。HTTP 状态码成功却内容为空,仍然是失败;curl 的连接退出码也不能代替应用结果判断。故障演练只在可控环境中进行:先将 B 设置为维护状态,观察新流量停止进入,再等待正在进行的下载结束。恢复后,确认检查通过且新请求重新分配。

从客户端到文件系统的观测位置
从客户端到文件系统的观测位置

把入口日志、后端访问日志和文件操作错误放在同个时间窗口比较。日志至少需要时间、请求标识、所选后端、状态码和耗时;入口应生成或规范传递请求标识,避免直接信任外部任意值。记录时区,核对主机时钟。没有关联信息时,看到入口出现超时、后端出现成功,并不能证明它们描述的是同次请求。

5 算法与一致性哈希的取舍

轮询按顺序选择后端,适合从简单模型开始理解。若 A、B 的处理能力和请求成本相近,轮询通常能提供清晰的分配结果。加权轮询允许能力较强的后端获得更多分配,例如权重为 2 与 1 时,长期分配比例可以接近 2:1。但权重不是吞吐保证:文件大小、缓存命中和磁盘等待不同,都可能让较少请求占用更多资源。

最少连接倾向于选择当前活动连接较少的后端,适合部分连接时长差异明显的场景。它使用的是负载的替代指标。某个后端只有少量连接,却都在传输大文件;另个后端连接较多,但请求很快完成,此时连接数不一定反映实际压力。HTTP 连接复用还会进一步改变连接与工作量的关系,因此必须核对算法统计的是哪类连接或请求。

哈希分配依据稳定键选择后端,例如经过规范处理的用户标识或资源键。相同键保持相近归属,有利于复用本地缓存。普通取模可以用“哈希值对后端数量取余”理解,但后端数量变化时,大量键可能重新分配,缓存会突然变冷。这里的键必须来自可信、稳定且适合分散的数据;如果所有请求都采用相同键,再好的散列函数也无法产生均衡。

一致性哈希的目标是让节点变化时,仅有部分键改变归属。可以把哈希空间想象成环,后端与请求键都落在环上,按约定方向找到负责节点。真实实现通常还会通过多个位置改善分布,但本课只需要理解减少重映射这个目标。它不会消除热点:单个热门文件的请求使用相同键,仍可能集中在负责它的后端,也不会复制该文件或保证会话持久化。

节点变化与键归属变化的示意
节点变化与键归属变化的示意

做选择时,把算法与状态位置放在一起判断。后端完全无本地会话,优先考虑易观测的分配策略;本地缓存价值明显,可以评估稳定键分配,同时检查热点;后端能力不同,考虑权重并定期复核。比较实验应使用相同请求集合、并发和时间窗口,观察总吞吐、错误率与尾部延迟。平均耗时下降但少数请求长时间等待,仍可能损害用户体验。

6 常见坑:把健康检查当可用性证明

最常见的误判是仪表盘全绿,就认定用户故障与后端无关。假设 /ready 只返回固定文本,而文件目录因权限变化不可读,检查仍然成功。此时要从失败操作出发:用户是否能建立连接,是否收到 HTTP 响应,响应内容是否完整。应用日志中的 Permission denied、No such file or directory 或打开文件过多,与网络超时指向不同的排查方向。

文件系统问题尤其容易被入口状态掩盖。服务进程运行身份可能与登录 shell 不同,管理员能读取文件,并不代表服务的 uid/gid 也能读取。使用 id、ls -ld /srv /srv/files 和 stat /srv/files/probe.txt 检查身份、目录访问权限与文件属性,路径应替换为实际实验路径。检查过程不要直接修改权限“试试看”,更不要把整个目录开放给所有用户来掩盖原因。

另种误判是检查越深越好。如果每次探测都依赖共享文件系统、身份服务和远端接口,某个共同依赖的短暂抖动可能让全部后端同时被摘除。应明确接口检查哪些必要条件,以及哪些问题通过真实请求监测发现。即使全部后端不可用,入口也应执行事先约定的行为,例如快速返回错误并告警;某些实现有特殊兜底规则,必须查清,不能凭经验假设。

从现象逐步缩小故障范围
从现象逐步缩小故障范围

超时与重试同样需要边界。连接超时约束建立连接的等待,响应等待与整个请求期限约束其他阶段,具体含义依实现而定。客户端只等 3 秒,入口却可能等待 30 秒,会造成客户端离开后入口仍占用资源。对于流式下载,短暂没有新字节和整个请求已经超时也不是相同情况,应根据服务模式配置并实际演练。

重试可能放大压力。入口在后端变慢时重试,客户端又重试,原本的少量请求可能变成多次执行。读取静态文件通常较容易重试,但创建目录、提交配置或触发任务不能仅凭连接断开就假定未执行。先判断操作能否安全重复,再限定重试次数、总时间和可重试条件。故障时优先保留证据,避免同时调整算法、检查阈值和超时,导致无法判断变化来自哪里。

7 案例:会话保持放大故障面

某运维文件门户有 4 个后端,管理员登录后浏览目录并下载诊断包。登录状态保存在后端本地,会话保持按来源地址实现。平时后端资源看似足够,某次集中排障时,大量管理员经由同个出口进入门户,被分配到 A。A 的连接数、内存和打开文件数快速增长,其余后端仍有空余资源,入口的总平均指标却没有显著异常。

随后 A 的下载开始等待。端口检查仍能建立连接,固定文本检查仍然返回成功,LB 因此继续把这组管理员送往 A。用户刷新页面增加了新请求,有些下载同时重试,排队继续扩大。这里会话保持把本应可分散的请求绑定到同个后端,浅层检查又没有及时反映服务退化,最终形成少数客户端集中受影响的故障面。

排查人员先按后端拆分请求数、错误率和耗时,再比较来源地址分布,发现 A 的来源高度集中。接着核对活动连接和进程资源限制,确认入口规则采用来源地址保持。这个证据组合比“CPU 高,所以扩容”更有解释力:新增后端不会自动改变已经固定的归属,问题还可能继续留在 A。日志中的来源信息也要先确认来自可信代理链。

会话绑定、资源压力与重试的关联
会话绑定、资源压力与重试的关联

处置需要兼顾会话。直接解除所有绑定可能让管理员集中重新登录,也可能丢失正在进行的本地操作状态。团队先限制重复请求,按维护流程减少 A 的新流量,并告知受影响用户可能需要重新登录;已经开始的下载在明确期限内排空。若存在必须由原进程完成的任务,则单独跟踪任务状态,不能依赖切换流量自动接续执行。

后续改进包括将需要跨后端共享的会话放到受控的共享机制,或者让服务使用可验证、可失效的会话凭据,同时保持认证边界清晰。本课不展开实现细节。验收要覆盖同个出口下的多个用户、单个后端退出以及恢复后重新加入。通过后端分布和用户成功率确认改进,不能只看入口重新变绿;会话共享的共同依赖也要纳入容量与故障评估。

8 把 LB 选型写进容量与故障域方案

容量方案首先要给“负载”具体单位。每秒请求数适用于部分短请求,活动连接数用于描述连接占用,文件服务还必须看每秒传输字节数和文件大小分布。测试报告应说明并发、请求类型和持续时间。单次下载很快并不能证明高并发能力,缓存中的小文件结果也不能直接外推到持续读取磁盘上的大型诊断包。

用简单例子理解失效余量:4 个后端在既定请求混合下,各自经过测试的可持续能力为每秒 100 个请求,正常总负载为每秒 240 个。均匀分配时每个约承担 60 个;失去 1 个后端后,剩余每个约承担 80 个。这个估算说明需要预留空间,但前提是分配足够均匀、资源可独立使用,且入口与共享依赖没有先达到上限。实际阈值仍需测量。

故障域决定余量是否真实存在。4 个进程若都运行在同台主机上,主机退出就会同时失去全部后端;分布在不同主机,却共同依赖单个文件系统,也可能同时无法服务。LB 自身也需要故障处理与稳定入口的接续方式。不能因为画出了多个方框就宣称高可用,必须标明共享电源、网络位置和依赖边界中与当前部署有关的部分。

容量余量与共同故障域
容量余量与共同故障域

选型记录还应覆盖运维接口:能否查询后端健康状态,能否先排空再摘除,是否支持配置检查与可靠回退,日志能否关联到后端。配置文件要受权限控制并纳入变更记录,避免 shell 临时修改后无人知道当前规则。发布前检查配置语法,再用小范围请求验证路径与权限;重载是否保留既有连接,需要依据所用实现确认。

验收可以围绕明确事件展开:正常请求、单个后端退出、后端变慢、入口重载和共享依赖异常。每个事件写下预期可观察结果、允许的恢复时间与终止演练条件。故障注入只作用于预先约定的实验对象,留有恢复命令和日志位置。这样方案里的算法、检查和容量才成为能够验证的承诺,而不只是名称清单。

9 从负载均衡走向 HTTP/3 与 Web 性能(S37)

到这里已经能够解释请求交给谁、后端何时停止接收流量,以及从哪些位置寻找证据。但用户看到页面慢,还可能来自连接建立、加密握手、网络往返、资源数量或浏览器加载顺序。LB 的后端处理耗时只是完整体验中的部分。即使后端资源分配均匀,客户端到入口的网络条件仍可能让用户等待很久。

继续保留分段观察习惯。用 curl 的时间字段可以粗略观察域名解析、建立连接、TLS 和首字节等阶段,但各字段有自己的定义,重定向与连接复用也会影响解读。实验应记录访问地址、协议版本以及是否复用连接。入口上的请求耗时与客户端总耗时不是相同测量边界,不应直接相减后把差值全部命名为“网络延迟”。

HTTP/3 使用 QUIC,并通常基于 UDP 传输,这会让入口协议支持、端口规则和观测方式出现新的问题。现有入口能够代理基于 TCP 的 HTTP,并不自动意味着能够接收 HTTP/3。迁移时要核对客户端到入口、入口到后端分别使用什么协议;入口支持新协议后,后端链路仍可能使用其他 HTTP 版本。具体连接机制与性能影响留到 S37 展开。

这里先检查连接归属这一项。假设入口前还有按 UDP 五元组分流的设备,客户端切换网络后,来源地址或端口可能变化,数据报就可能被送到另个入口节点。原节点保存的 QUIC 连接状态不会自动出现在新节点上;即使两个节点健康检查都通过,这条连接仍可能中断。支持连接迁移需要核对入口是否支持依据连接标识维持归属,以及前置设备如何配合,不能直接沿用 TCP 的连接分配假设。实验时可在受控客户端切换网络,关联入口节点日志,观察连接是否延续、是否重新握手及下载是否完整;再分别核对 UDP 放行规则和转发设备的空闲超时,避免把数据报被丢弃误判为后端处理缓慢。

协议升级也不会修复会话只存在于某个进程、检查接口过浅或共享磁盘容量不足。进入下一课前,应能够画出客户端、入口、后端和依赖的路径,说明调度单位,指出检查盲区,并区分用户等待与后端处理。带着这些边界研究 Web 性能,才能判断改善发生在哪段路径,以及是否同时改变了故障处理方式。

放进方案里

为运维文件门户写方案时,先写访问对象与成功条件:管理员经由 HTTPS 入口浏览目录并下载诊断包,成功意味着权限校验正确、文件内容完整且在约定期限内完成。注明入口地址、监听端口、后端池和返回路径,说明 TLS 在哪里终止,入口到后端是否加密,以及哪些代理来源信息可以被信任。把管理接口的访问范围单独写清楚。

选择 HTTP 7层代理的理由是需要按路径分流并记录请求结果。以轮询作为初始策略,用测试结果决定是否设置权重;会话能够跨后端使用后,再取消不必要的绑定。定义就绪接口检查的必要条件、检查间隔、失败与恢复阈值,并说明检查不能证明完整下载成功。用受控的小文件下载监测补足用户路径证据,记录状态、大小与内容校验结果。

容量部分写明请求混合、并发连接、带宽和可持续处理能力,以失去 1 个后端后仍能承接目标负载为验收场景。把入口、后端主机及共享文件系统的故障域画出来,明确共同依赖的限制。维护操作先停止新流量,再等待既有下载排空;写明等待期限、超期处理和恢复加入条件,避免现场临时决定是否断开连接。

观测部分统一时区和请求标识,入口记录所选后端、状态与耗时,后端保留访问及文件错误日志。告警同时观察用户失败率、各后端分布、活动连接与资源压力。变更前检查配置,保存可回退版本;演练覆盖后端退出、变慢和恢复。每项演练写明预期现象、恢复步骤与停止条件,由实际证据确认方案达到目标。

10 小结:下一步 HTTP/3 与 Web 性能(S37)

负载均衡通过稳定入口和调度规则组织流量,但效果取决于调度单位、状态位置、检查质量与剩余容量。4层与7层决定入口能够依据什么信息行动,健康检查决定何时调整分配,会话保持则会限制这种调整的自由度。

自测时尝试回答:端口检查成功,为什么文件下载仍可能失败?新增后端后,为什么热点可能没有转移?摘除后端后,已有下载会怎样?如果无法回答,就回到请求路径、日志和明确的摘除语义中寻找证据。S37 将沿着客户端到入口的路径,继续讨论 HTTP/3 与 Web 性能。

同阶段相关课

AI 答疑

默认关闭。启用后,只发送本课标题、最近阅读的屏幕内正文摘录(最多 6000 字)与本次问题。

配置自己的服务