做运维这些年,跟 LVS 打交道的次数数不过来,但每次新同事接手集群,问的第一批问题里准有“调度算法怎么看”“生产环境到底该用哪个”。市面上讲 LVS 原理的文章不少,可真到“查看现有配置”和“按业务选算法”这两步,能直接抄作业的总结不多。这篇文章就把我的实操经验理一理:既讲怎么把当前调度算法扒出来,也把常用算法的原理、适用场景和选型逻辑完整过一遍,最后附上验证命令和避坑记录。不论你刚接触负载均衡,还是已经维护着上百台 RS 的集群,这篇都值得花十分钟看完。
1. 先把调度算法“捞”出来:查看的三种方式
调度算法是 LVS 的核心决策器,它决定了每个新连接被转发到哪台后端 RS。既然是决策器,第一步当然是看清楚集群当前用的什么策略。这里分享三种查看方式,按使用场景选就行。
1.1 命令行查看:ipvsadm -l 与 ipvsadm -L -n
最直接的方式就是 ipvsadm 命令。如果只是想知道“当前 LVS 集群有哪些 VIP、转发模式是什么、调度算法是什么”,用ipvsadm -l就够了:
ipvsadm -l IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.10.10:80 wrr -> 192.168.10.11:80 Route 1 0 0 -> 192.168.10.12:80 Route 1 0 0注意看第二行输出里的wrr,这就是当前虚拟服务器使用的调度算法。这条命令看“人话”最合适,Scheduler 字段直接列在那儿,一眼定位。
如果是在脚本里解析输出,或者想把内核里的原始状态拉出来看,用ipvsadm -L -n加-n关闭反解 DNS,输出格式更干净,不会因为主机名解析超时卡住:
ipvsadm -L -n IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.10.10:80 wrr -> 192.168.10.11:80 Route 1 0 0 -> 192.168.10.12:80 Route 1 0 0-l和-L在这类场景下效果几乎一样,-L在手动维护脚本里更常见。配合-n后输出稳定,适合扔给 awk、grep 做二次处理。
1.2 从内核里直接刨数据:/proc/net/ip_vs
除了 ipvsadm,还有一个更底层的信息源:/proc/net/ip_vs。这个文件是 LVS 内核模块直接导出的状态接口,内容比 ipvsadm 更原始,包含完整的调度算法名称、超时时间、连接统计:
cat /proc/net/ip_vs IP Virtual Server version 1.2.1 (size=4096) TCP 000A0AC0:0050 wrr -> 000A0AC0:0051 Route 1 0 0 -> 000A0AC0:0052 Route 1 0 0第二行的wrr同样是调度算法。这里的 IP 和端口是十六进制表示的,000A0AC0换算过来就是192.168.10.10,0050是十进制的 80。说实话这个格式对人不友好,但它的价值在“快”——ipvsadm 底层也是读这个文件,如果你写监控脚本或者排查内核态异常,直接读 proc 文件更轻量,也没有依赖 ipvsadm 版本的差异。
还有一个实用技巧:/proc/net/ip_vs末尾有几行超时配置,分别对应 TCP 空闲超时、TCP 持久连接超时和 UDP 超时。这些超时时间对调度效果影响很大,后面第 3 节细说。
1.3 生产环境下的查看建议
排查问题时我习惯先ipvsadm -L -n --stats看流量统计,再ipvsadm -L -n --rate看速率,最后用cat /proc/net/ip_vs确认算法和超时。三层配合,既能知道“配置是什么”,还能看出“算法到底把连接分成了什么样”。只问“算法配了什么”不看分发结果,是运维里最容易犯的错。
注意:LVS 配置是内存态的,
/proc/net/ip_vs和 ipvsadm 显示的都是当前内核运行配置。如果改了 keepalived 配置文件但没 reload,看到的还是旧算法,排查时先确认 keepalived 状态,别被缓存配置带偏。
2. 算法逐个拆解:原理、适用场景与选型逻辑
LVS 调度算法有静态和动态两大类。静态算法不关心后端 RS 的实时负载,动态算法会参考当前连接数或权重来计算。理解这个分类,是选型的第一步。
2.1 静态算法与动态算法的分类框架
静态算法包括 RR、WRR、DH、SH 四种。它们的共同特点是“不看后端状态”,按固定规则分发,实现简单,也没有额外收集负载信息的开销。但代价是遇到后端性能不均衡时,容易“能者多劳变成能者累死”。
动态算法包括 LC、WLC、SED、NQ、LBLC、LBLCR 六种。它们以“当前连接数/权重”为核心依据,能感知后端的忙闲程度。动态算法里最有代表性的是 WLC(加权最少连接),这也是 LVS 的默认算法。
这里放一张速查表,后续选型直接对照:
| 算法 | 类别 | 核心逻辑 | 优点 | 适合场景 |
|---|---|---|---|---|
| RR | 静态 | 轮询,挨个分发 | 实现简单、绝对均衡 | RS 配置完全一致、无状态服务 |
| WRR | 静态 | 按权重轮询 | 能体现后端性能差异 | RS 性能不同,但请求处理时长相近 |
| LC | 动态 | 连接数最少优先 | 能感知实时负载 | 长连接服务,后端处理能力一致 |
| WLC | 动态 | 连接数/权重最小优先 | 动态且兼顾权重 | 通用场景,LVS 默认算法 |
| SED | 动态 | 最少期望延迟优先 | 避免 WLC 的“连坐”问题 | 请求处理时间差异较大 |
| NQ | 动态 | 最少连接,若同等则首次分配 | 改进版 SED | 快速恢复的短连接场景 |
| SH | 静态 | 源地址哈希 | 同一 IP 固定到同一 RS | 需要会话保持的业务 |
| DH | 静态 | 目的地址哈希 | 同一目标 VIP 固定到同一 RS | 多 VIP 或多端口映射场景 |
| LBLC | 动态 | 基于本地最少连接 | 考虑目标 IP 的局部性 | 缓存类服务,如 Memcached |
| LBLCR | 动态 | 带复制的本地最少连接 | 支持缓存复制 | 缓存命中率要求高的集群 |
2.2 高频算法详解:RR、WRR、LC、WLC 的选型区别
RR(Round Robin)是所有算法的基础,把请求按顺序循环分给每台 RS。配置完全一致的后端用 RR 非常合适,例如无状态的 API 服务集群,请求量均匀且每台机器规格相同。RR 的问题在于它不感知后端负载,如果其中一台 RS 因硬件故障响应变慢,新连接依然会正常分给它,最终拖垮整个集群。生产环境里纯 RR 很少单独用,往往配合健康检查来剔除异常节点。
WRR(Weighted Round Robin)在 RR 基础上增加了权重概念。权重代表“这台机器应该承担多大比例的流量”。假设 RS1 是 8 核机器,RS2 是 4 核机器,可以把 RS1 权重设为 2,RS2 设为 1,LVS 会按 2:1 的比例轮流分发。WRR 的短板是它只认权重,不认实时状态,哪怕 RS2 已经快被打满,只要按权重该轮到达,还是会继续发过去。
LC(Least Connections)开始进入动态算法领域,它每次都挑当前活跃连接数最少的 RS。这个思路很直觉:连接越少说明越闲。但 LC 有个隐藏问题:如果请求处理时间差异很大,一个长时间占用连接的下载请求会让某台 RS 连接数虚高,导致这台机器长期被“冷落”,而其他机器忙得不可开交。
WLC(Weighted Least Connections)是 LC 的加权版,也是大多数人的默认选择。它的计算公式是“连接数除以权重,取最小者”。这个除法逻辑能避免权重形同虚设。比如 RS1 权重 2,当前连接 100;RS2 权重 1,当前连接 60。按 WLC 计算:RS1 的100/2=50,RS2 的60/1=60,LVS 会把新连接分给 RS1,尽管它的绝对连接数更多,但单位权重下的负载反而更低。这个“除以权重”的细节是理解 WLC 的关键。
2.3 被低估的 SED、NQ、SH 和 LBLC 算法
SED(Shortest Expected Delay)是对 WLC 的修正。WLC 的问题在于新连接到来时只做一次计算,如果某台 RS 连接数一直偏高,它在除法后依然可能不是最小的,导致新连接永远不进这台机器。SED 的计算方式更多考虑“如果我把这个新连接给你,你完成它的期望时间是多少”,能更快把新连接引向相对空闲的 RS。这个区别在请求处理时长波动大的业务里体现得很明显。
NQ(Never Queue)是 SED 的补充,本质是“如果集群里有任何一台 RS 连接数为 0,新连接优先分给它”。这个机制非常适合突发流量场景:新扩容的机器连接数自然是 0,NQ 会立刻把流量导过去,而不是等 WLC/SED 慢慢“看到”它。缺点是如果所有 RS 都有连接,NQ 会退化成 SED 行为。
SH(Source Hashing)是“会话保持”场景的王牌。它计算源 IP 的哈希值,同一个源 IP 会被固定分发到同一台 RS。最典型的应用是带用户登录态的业务,比如购物车服务,用户第一次请求落到 RS1,后续请求也必须在 RS1 才能拿到完整会话。SH 的缺点是哈希分布可能不均衡,后端 RS 扩缩容时哈希重排会造成部分用户的会话漂移,生产环境扩缩容时需要谨慎评估。
LBLC(Locality-Based Least Connections)和 LBLCR 主要面向缓存类服务。LBLC 的目标是“同目标 IP 的请求尽量打到同一台 RS,同时兼顾连接数”,适合 Memcached、Redis 这种需要访问局部性的场景。LBLCR 更进一步,它允许缓存数据在多台 RS 间复制,命中率高了但内存开销也上去了。这两种算法在常规 Web 集群里用得不多,但如果你维护的是缓存集群,它们比 WLC 靠谱得多。
3. 实操:调度算法的配置、调整与验证
查看和理解了算法,接下来就得动手改配置。这里有一个很多人忽略的事实:LVS 本身只是内核模块,真正做配置管理的是 ipvsadm 工具或 keepalived 这类用户态程序。改算法有两条路,我分别说明,并给出生产环境最稳妥的操作顺序。
3.1 新装 LVS 时指定算法,以及运行时动态修改
新配置一个 LVS 虚拟服务器时,在ipvsadm -A命令后面直接用-s参数指定调度算法:
ipvsadm -A -t 192.168.10.10:80 -s wlc ipvsadm -a -t 192.168.10.10:80 -r 192.168.10.11 -g -w 1 ipvsadm -a -t 192.168.10.10:80 -r 192.168.10.12 -g -w 2这段命令的含义是:创建一个 VIP 为192.168.10.10:80的虚拟服务器,调度算法用 WLC;添加真实服务器192.168.10.11,使用 LVS 的 DR 转发模式(-g),权重 1;再添加192.168.10.12,同样 DR 模式,权重 2。
如果 LVS 服务已经在运行,需要把调度算法从 WLC 改成 SH,用-E参数修改虚拟服务器配置:
ipvsadm -E -t 192.168.10.10:80 -s sh修改后立刻生效,不需要重启任何服务。注意-E修改的是当前内存配置,服务器重启后失效;如果要永久生效,必须同步修改 keepalived 配置文件或初始化脚本。
3.2 修改算法的“平滑性”问题:旧连接怎么办
改调度算法最容易被忽略的,是“现有连接的去向”。调度算法只影响新连接的分发,已经建立的连接会继续走原来的 RS,直到连接自然断开或超时。这意味着如果你从 WLC 改成 SH,集群里所有旧连接依然按照 WLC 的规则留在原 RS,只有新连接会按 SH 规则走哈希。
这一点在涉及会话保持的业务里尤其重要。假设用户 A 的旧连接在 RS1,改算法后他的新连接被哈希到了 RS2,如果业务没有做 Session 同步,用户会被强制登出。所以生产环境切换调度算法前,建议先确认业务是否依赖会话保持;如果是,要么提前做好 Session 共享(如 Redis 集中存储),要么在低峰期切换并配合连接超时快速回收旧连接。
还有一个细节:ipvsadm -E修改算法时,LVS 的Flags字段也会被重置。有些文档里提到-s后面可以跟-p开启持久连接,如果修改时忘了带-p,原本启用的持久连接会被悄悄关掉,对业务可能造成不小的冲击。建议修改后用ipvsadm -L -n复核 Flags 列。
3.3 超时时间与调度算法的“联动效应”
连接能不能按预期被调度,还依赖于超时参数。回到/proc/net/ip_vs文件,末尾有三组超时值:TCP空闲超时、TCP持久连接超时、UDP超时。默认值通常是 900、300、300 秒(也就是 15 分钟、5 分钟、5 分钟)。
考虑一个场景:A 集群用的是 WLC 算法,后端跑的是长轮询服务。客户端每 30 秒发起一次请求保持连接不断开,这个连接永远达不到 15 分钟的空闲超时,就会一直挂在某台 RS 上。WLC 按连接数分发,长连接全被老节点占着,新节点的连接数始终上不来,流量完全倾斜。要解决这个问题,除了调整超时时间参数,另一个思路是让后端服务主动设置 Keep-Alive 超时,断开空闲连接,让连接数回归真实负载。超时参数不是死的,但没有统一的推荐值,必须看业务心跳频率来调整。
3.4 一次完整的算法调整操作记录
我在生产环境里做过一次从 WLC 切到 SED 的调整,过程可以给大家参考。
先执行以下命令观察当前统计:
ipvsadm -L -n --stats查看每台 RS 的 ActiveConn 和 InActConn 分布。如果发现 RS1 的 ActiveConn 长时间维持在 RS2 的三倍以上,且 RS1 的 CPU 负载明显偏高,RS2 却很闲,说明 WLC 对这种请求处理时长差异大的业务不够敏感。
接着把算法切到 SED,观察 5 分钟和 30 分钟后的连接分布:
ipvsadm -E -t 192.168.10.10:80 -s sed ipvsadm -L -n --stats切换后,新连接会逐渐向 RS2 倾斜,ActiveConn 的差距会慢慢缩小。这里有个重要心得:调度算法切换后不是立即均衡,动态算法需要“预热”时间,让连接数统计反映新的调度结果。至少观察 15 到 30 分钟再做结论,不要刚切完 1 分钟觉得效果不明显就急着回滚。
4. 业务场景反推算法:决策路径与配置组合
算法不能脱离业务谈优劣。同一个集群,跑的是静态页面还是 WebSocket 长连接,选出的算法可能完全不同。这里提供一个可复用的决策路径,我每次搭建新集群都按这个思路走。
4.1 决策五步法:先从业务需求倒推算法
第一步,判断后端是否无状态。无状态服务只要有健康检查,RR 或 WRR 就够用;有状态服务要考虑会话保持,优先 SH 或持久连接。
第二步,看请求处理时长是否稳定。所有请求都是几十毫秒内完成,WLC 的不敏感问题不明显;如果请求时长从毫秒到分钟都有,SED 或 NQ 更合适。
第三步,看后端机器性能是否一致。性能一致用 RR 或 LC;不一致必须用 WRR 或 WLC,权重才能体现作用。
第四步,看是否需要会话保持。需要的话,SH 是最简单可靠的方案;不想用哈希导致的不均衡,可以改用 LVS 的持久连接功能(-p),它在 WLC 基础上增加会话保持能力。
第五步,看 LVS 层是否还需要承载其他协议。TCP 和 UDP 服务混布时,UDP 连接的超时机制和 TCP 不同,建议按协议拆分虚拟服务器,分别配置算法。
4.2 典型业务的算法推荐组合
这里把常见的业务类型和推荐算法整理成一张表,是我维护过多套集群后的经验值,不是教科书标准答案:
| 业务类型 | 推荐算法 | 原因 |
|---|---|---|
| 静态资源 Web 集群 | WRRSKIP | 请求快且均匀,权重能控制流量比例 |
| 无状态 API 网关 | WLC | 动态感知后端负载,默认算法足够稳 |
| 登录态购物车 | SH 或 WLC + 持久连接 | 同一用户固定到同一 RS,保证会话连续 |
| WebSocket 长连服务 | LC 或 WLC | 连接数能真实反映后端承载量 |
| Memcached 集群 | LBLC / LBLCR | 缓存访问需要局部性,减少跨机命中 |
| 视频点播/大文件下载 | SED | 请求时长差异大,避免一台 RS 连接数虚高 |
| 数据库中间层 | NQ | 扩缩容频繁,新 RS 能第一时间接到流量 |
多嘴提醒一句:不要为了“用上冷门算法”而强行选型。LBLC、LBLCR、DH、SH 都有明确的适用前提,如果业务不需要会话保持,也不跑缓存集群,坚持用 WLC 反而是最安全的选择。
4.3 keepalived 场景下的算法协同调整
LVS 大多与 keepalived 一起工作,keepalived 不仅管 VIP 漂移,还负责同步 LVS 配置。调整算法时,除了用 ipvsadm 直接改内存配置,还需要同步修改 keepalived 配置文件,否则 keepalived reload 后会把算法改回去。
keepalived 配置里调整算法的位置是virtual_server块中的scheduler字段:
virtual_server 192.168.10.10 80 { delay_loop 6 lb_algo wlc lb_kind DR persistence_timeout 0 protocol TCP real_server 192.168.10.11 80 { weight 1 TCP_CHECK { connect_timeout 3 } } real_server 192.168.10.12 80 { weight 2 TCP_CHECK { connect_timeout 3 } } }修改lb_algo字段后,执行systemctl reload keepalived使配置生效。这里有一个很重要的差异:keepalived reload 会重新下发全部 LVS 规则,如果你在 ipvsadm 里手动加过规则,reload 后这些规则会被清掉。最好的实践是改配置直接在 keepalived 里改,然后 reload,不要两种管理方式混用。
4.4 LVS 调度算法与 Nginx 负载均衡的配合分工
总有人纠结“有了 LVS 还要不要 Nginx”。这两层解决的不是同一个问题:LVS 工作在四层,基于 IP 和端口分发,性能极高,但不理解 HTTP 请求内容;Nginx 工作在七层,能根据 URL、Header、Cookie 做路由。
我的常见架构是“LVS 做流量入口,Nginx 做业务路由”。用户请求先到 LVS,LVS 按调度算法把连接分发给一组 Nginx;Nginx 再根据请求路径把请求转发给对应的后端应用。这种情况下,LVS 的调度算法只需要考虑“让 Nginx 负载均衡”,不需要关心具体业务。通常 Nginx 集群的 LVS 调度用 WLC 就足够了,因为四层转发只跟连接数相关,Nginx 本身处理请求很快,连接数基本能反映压力。
如果只有 LVS 直接分发到应用服务器,没有七层网关,才需要更精细地根据业务类型选择调度算法。换句话说:架构层数越多,LVS 调度算法的复杂程度反而越低。
5. 常见问题与排查技巧实录
这部分整理了我在实际运维中反复踩过和解决的坑,希望能帮你少走弯路。
5.1 典型问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 后端 RS 连接数明显不均衡 | 算法不匹配业务,长连接堆积 | 换 SED/NQ,或调整 RS 权重 |
| 修改算法后不生效 | keepalived reload 覆盖了 ipvsadm 配置 | 在 keepalived 配置里改,统一入口 |
| 用户会话经常断开 | SH 算法下 RS 故障导致哈希重映射 | 后端做会话共享,或用持久连接 |
| 新扩容 RS 一直没流量 | WLC 对连接数为 0 的节点感知慢 | 改用 NQ,或手动把连接导过去 |
| 连接数正常但吞吐很低 | 单条连接处理慢,连接数不代表压力 | 观察 CPU/带宽,改用 SED 权重策略 |
| 改了持久连接超时后会话失效 | -E修改规则时丢了-p参数 | 修改时重新指定-p和超时时间 |
| TCP 连接大量 TIME_WAIT | 后端短连接关闭过快 | 调整系统net.ipv4.tcp_fin_timeout,或优化应用 Keep-Alive |
5.2 排查技巧:用统计命令做“调度效果体检”
判断当前算法到底合不合适,不能只看配置文件,要看实际分发结果。我一般分三步走:
第一步,看总体连接分布:
ipvsadm -L -n --stats关注ActiveConn列。如果某台 RS 的 ActiveConn 长期是另一台的数倍以上,说明算法响应不及时或权重配得不对。注意 ActiveConn 和 InActConn 的区别:ActiveConn 是当前正在活跃的连接,InActConn 是已经建立的但还未开始传输数据的连接,两者都能反映负载,但 ActiveConn 更接近真实压力。
第二步,看速率趋势:
ipvsadm -L -n --rate每秒连接数(CPS)、每秒入包数(InPPS)、每秒出包数(OutPPS)这些数据能反映集群的流量水位。如果 CPS 很低但 OutPPS 很高,说明请求大包多,可能存在大文件传输,需要结合带宽监控判断是否真的过载。
第三步,看连接追踪表:
ipvsadm -L -n -c这个命令输出当前的连接跟踪条目,能看到每个连接分发到了哪台 RS。如果发现同源 IP 的请求分散在不同 RS 上,而业务又需要会话保持,说明算法或持久连接配置存在问题。
5.3 关于同名概念的澄清:别把负载均衡 LVS 和版图 LVS 搞混
搜索 LVS 调度算法的过程中,会看到一批完全不相干的同名内容,例如芯片设计领域中的 LVS(Layout vs Schematic,版图与原理图一致性检查)。这是两个完全不同的概念:负载均衡 LVS 是 Linux Virtual Server,运行在 Linux 内核里,负责流量分发;而芯片设计中的 LVS 是 EDA 工具里的一项检查流程,用来比对版图和电路原理图是否一致,配合 DRC(设计规则检查)一起保障芯片设计正确性。
排查问题前先确认自己在讨论哪个 LVS,别被技术社区里的话题混淆带偏。两种场景的排查方法、工具链和知识体系互不相干,文章里如果搜到“calibre 跑 LVS”“反相器 DRC LVS”这类内容,说明已经切到了 EDA 领域,用不上负载均衡那套命令。
5.4 我的排障顺序与经验沉淀
每次出问题,我的排查顺序是:先确认 keepalived 主备状态,再确认 VIP 是否在当前节点;然后看 LVS 统计确认流量是否到达,接着看 RS 的健康检查是否全部通过;最后才去分析调度算法是否合理。这个顺序能避免“算法没问题,其实流量根本没到 LVS”的乌龙情况。
调度算法的调优没有银弹,但有一个原则:先量化,再选型。每次调整算法前先记录当前各 RS 的连接数和负载指标,调整后观察同等时间窗口的变化,有数据做支撑,后期复盘也会轻松很多。
最后再分享一个我的习惯:每套集群的调度算法、权重和超时参数,我都要求记录到集群的变更文档里,并定期跑ipvsadm -L -n --stats生成基线数据。这样下次有人问“这个集群为什么用 SED 不用 WLC”,翻记录就能给出明确答案,而不是靠回忆讲故事。负载均衡的调优更像养花,不是一次配置就完事,得持续观察、浇水、修剪,才能保证集群长时间稳定运行。