nginx默认加权轮询仅保证长期请求比例符合权重,不控制序列节奏,易致瞬时压力不均;真正平滑需用openresty+lua实现swrr算法,维护current_weight与effective_weight动态调度,并配合健康检查和连接复用等配置优化。

Nginx 默认的加权轮询(Weighted Round Robin)只保证长期请求比例符合权重,但不控制请求序列的分布节奏——比如权重 3:1 的两个节点,可能连续发 3 次给高权节点再发 1 次给低权节点,造成瞬时压力不均。要真正“平滑”,就得让请求按权重比例均匀穿插,比如 A,A,B,A,A,B,A… 这类节奏。这需要跳出默认调度机制,引入状态感知和动态权重调整。
理解默认加权轮询的局限性
Nginx 开源版 upstream 模块中的 weight 参数仅影响累计请求占比,调度器无运行时状态、不记录已分配次数、也不维护节点当前负载或响应延迟。它本质是静态权重映射,无法做到请求级的节奏控制。
- 权重 5:3:2 的三台后端,长期看请求比接近 50% : 30% : 20%,但短期内可能出现集中打到某一台的情况
- 不感知节点健康状态,故障节点仍可能被轮到(除非配合 max_fails + fail_timeout 主动剔除)
- 权重修改后必须 reload 配置,无法热更新
用 OpenResty + Lua 实现真正平滑调度
这是目前最成熟、可控性最强的方案。通过 balancer_by_lua_block 在每次请求进入 upstream 阶段时,运行自定义 Lua 逻辑,基于 SWRR 经典算法选择后端。
- 为每个节点维护
current_weight(当前权重)和effective_weight(原始权重),每轮将所有节点current_weight += weight - 选出
current_weight最大的节点,将其current_weight -= total_weight - 使用
ngx.shared.dict存储节点状态,确保多 worker 间共享调度上下文 - 可叠加健康检查结果(如从 shared dict 读取节点存活标记),跳过异常节点
升级 Nginx Plus 或采用增强型社区模块
如果你使用的是商业版 Nginx Plus R20+,它虽未直接暴露 “SWRR” 名称,但提供了更精细的调度能力:
- least_time $upstream_header_time 可结合权重,优先选响应快且权重高的节点
- random two least_conn 在最小连接数的两个候选中随机选一个,再按权重加权,降低突发倾斜
- 搭配 hash $request_id consistent 可实现带权重的一致性哈希,适合有会话粘性要求但又需权重调节的场景
开源社区也有像 nginx-upstream-jvm-route 或 lua-upstream 这类模块,支持运行时更新权重、主动探测节点延迟,并在 Lua 层封装 SWRR 调度器,无需重编译 Nginx。
配置层面的必要配合项
无论采用哪种平滑逻辑,基础配置优化都不能少,否则算法效果会被底层机制抵消:
- 启用 keepalive 32 复用连接,减少 handshake 开销对调度节奏的干扰
- 设置 max_fails=2 fail_timeout=15s,让异常节点快速退出调度循环
- 在 location 块中添加 proxy_next_upstream error timeout http_500,失败时自动重试下一节点
- 避免混用 ip_hash 或 hash $remote_addr,它们会覆盖 upstream 的权重逻辑











