Nginx原生upstream不支持Lua动态权重,需用OpenResty+Lua在balancer_by_lua_block中读取shared_dict指标、按公式计算权重并set_current_peer实现毫秒级动态选点。

在 Nginx 的负载均衡阶段,原生的 upstream 模块不支持直接用 Lua 动态计算权重并干预节点选择逻辑。但可以通过 lua-upstream-nginx-module(需额外编译)或更通用、无需定制模块的方案——基于 Lua 的 upstream 重写 + 变量控制 来实现自定义加权轮询。核心思路是:绕过原生轮询,用 Lua 决定每次请求该发往哪个后端,并通过 proxy_pass 显式转发。
用 lua-resty-upstream-healthcheck + 自定义选择逻辑
这是最实用且生产就绪的方式。借助 OpenResty 提供的 lua-resty-upstream 和健康检查模块,你可以维护一个可读写的 upstream 列表,并在 access_by_lua_block 中动态选节点:
- 预先在 Lua 全局或 shared_dict 中维护各后端的当前权重、历史调用次数、响应延迟等指标
- 每次请求进入时,运行你的加权轮询算法(比如平滑加权轮询 SWRR、基于实时 RT 的动态权重、或结合 CPU/错误率的复合权重)
- 将选中的后端地址写入一个 Nginx 变量(如
$backend_addr),再在proxy_pass中引用它:proxy_pass http://$backend_addr; - 注意:必须关闭原生 upstream 的自动负载均衡,即不要在
proxy_pass中直接写http://my_upstream
用 set_by_lua_block 预计算目标地址(轻量级方案)
适合权重变化不频繁、逻辑较简单的情况。利用 set_by_lua_block 在 rewrite 阶段生成目标地址:
- 把后端列表和权重配置为 Lua table(或从 shared_dict 加载)
- 实现一个原子递增计数器(用
ngx.shared.dict:incr)模拟“轮询槽位”,再结合权重做模运算或累积概率匹配 - 例如:权重 [3, 1, 2] → 总权重 6 → 生成 0~5 的随机/递增索引,按前缀和 [3,4,6] 划分区间,确定节点
- 结果赋给变量:
set_by_lua_block $backend { return choose_backend(...) };,然后proxy_pass http://$backend;
注意事项与避坑点
这类方案不是“插件式增强”,而是重构了流量分发路径,因此需特别注意:
-
健康检查要自己做:原生 upstream 的
max_fails/fail_timeout失效,需用lua-resty-upstream-healthcheck或定期调用ngx.location.capture探活 -
共享状态需同步:多 worker 下的计数器、权重更新必须走
shared_dict,避免各 worker 独立轮询导致倾斜 -
避免阻塞:Lua 里不能做 DNS 解析、HTTP 请求等阻塞操作;后端地址建议预解析为 IP,或用
resolver+lua-resty-dns异步查 -
超时与重试要手动处理:原生 upstream 的
proxy_next_upstream不生效,失败后需在 Lua 中捕获 error 并手动 fallback
一个最小可行示例(SWRR 简化版)
假设三个后端,权重分别为 5、3、2:
lua_shared_dict backend_state 10m;
upstream dummy { server 127.0.0.1:1; } # 占位,实际不用
<p>server {
location /api/ {
set_by_lua_block $target {
local dict = ngx.shared.backend_state
local backends = {{"192.168.1.10:8080", 5}, {"192.168.1.11:8080", 3}, {"192.168.1.12:8080", 2}}
local total_weight = 10
local counter = dict:incr("swrr_counter", 1, 0)
local idx = counter % total_weight
local sum = 0
for i, b in ipairs(backends) do
sum = sum + b[2]
if idx < sum then
return b[1]
end
end
return backends[1][1]
}
proxy_pass <a href="https://www.php.cn/link/7106f0a45b309d615ef19c4b238e5365">https://www.php.cn/link/7106f0a45b309d615ef19c4b238e5365</a>;
proxy_set_header Host $host;
}
}


















