Nginx默认不支持平滑加权轮询(SWRR),其内置weighted round-robin仅保证长期权重比例,不控制请求序列顺序;需通过OpenResty+Lua自定义balancer_by_lua_block实现真正平滑调度,或升级至Nginx Plus R20+结合高级策略逼近效果。

要实现 Nginx 模块级的平滑加权轮询(Smooth Weighted Round Robin),核心在于正确配置 upstream 块中的 weight 和启用 ip_hash 之外的更智能调度逻辑——但需注意:Nginx 官方默认的 round_robin 调度器本身**不提供“平滑”权重调度**(即不会严格按权重比例连续分发,而是累积误差后动态调整)。真正的“平滑加权轮询”需依赖第三方模块或高版本 Nginx 的增强能力。
理解 Nginx 默认 weighted round-robin 的行为
Nginx 默认的加权轮询是基于静态权重的简单分配:节点 A 权重为 3、B 为 1,则每 4 次请求中 A 接收 3 次、B 接收 1 次。但它不保证请求序列平滑(例如可能连续发 3 次给 A,再 1 次给 B),在突发流量或节点响应时间差异大时,容易造成瞬时倾斜。
- 权重仅影响长期请求占比,不控制单次调度顺序
- 无运行时状态维护,不感知后端健康、延迟或负载
- 不支持动态权重调整(需 reload 配置)
使用 nginx-upstream-jvm-route 或 lua-upstream 实现平滑调度
若需真正平滑(即按权重比例均匀穿插请求,如权重 3:1 → A,B,A,A,B,A…),推荐通过 OpenResty + Lua 自定义 upstream 调度逻辑:
- 利用
balancer_by_lua_block在每次请求时计算下一个节点,维护全局计数器和最大公约数(GCD)调度周期 - 参考经典 SWRR 算法:为每个节点维护
current_weight和effective_weight,每轮将所有节点current_weight += weight,选最大者,再减去总权重 - 示例片段(需配合 shared dict 存储状态):
balancer_by_lua_block {
local balancer = require "ngx.balancer"
local shdict = ngx.shared.upstream_states
-- 获取并更新平滑权重状态,选择最优后端
local peer = select_smooth_peer()
balancer.set_current_peer(peer.host, peer.port)
}
升级到 Nginx Plus 或使用社区增强版
Nginx Plus R20+ 内置了 least_time、random with two least_conn 等高级策略,虽未直接命名 “SWRR”,但其 hash $request_id consistent; + 权重组合可逼近平滑效果;而开源社区有模块如 nginx_upstream_check_module 配合自定义脚本也能辅助实现带健康检查的加权调度。
- 启用
keepalive和max_fails/fail_timeout提升容错性 - 结合
proxy_next_upstream error timeout http_500实现失败自动漂移 - 用
stub_status或nginx-module-vts实时观测各节点实际请求数与权重偏差
配置示例:兼顾权重、健康与基础平滑
以下是一个生产可用的 upstream 配置,虽非理论上的“完全平滑”,但在实践中能显著改善默认轮询的毛刺问题:
upstream backend {server 192.168.1.10:8080 weight=3 max_fails=2 fail_timeout=10s;
server 192.168.1.11:8080 weight=1 max_fails=2 fail_timeout=10s;
server 192.168.1.12:8080 weight=2 max_fails=2 fail_timeout=10s;
keepalive 32;
}
搭配 proxy_set_header Connection ''; 和 proxy_http_version 1.1; 复用连接,减少新建开销,间接提升权重分布稳定性。










