Nginx无法动态修改upstream server的weight,但可通过balancer_by_lua_block结合实时CPU数据实现加权随机或最小负载调度:采集后端CPU使用率(HTTP接口/Prometheus/本地文件),反向计算权重,调用set_current_peer()手动选上游。

Nginx 本身不支持在运行时动态修改 upstream 中 server 的 weight,尤其不能直接根据后端真实 CPU 使用率实时调整。但可通过 Lua 脚本配合外部数据源(如 Prometheus、HTTP 接口或本地采集)实现“逻辑权重”调度,本质是绕过原生 weight 字段,改用 balancer_by_lua* 钩子自主选上游。
核心思路:用 balancer_by_lua* 替代静态 weight
原生 upstream 的 weight 是编译期/配置期固定值,无法被 Lua 修改。真正可行的方式是:
- 启用
lua-upstream-nginx-module(或使用 OpenResty 内置支持) - 定义 upstream 时使用
least_conn或ip_hash占位,实际调度逻辑完全交由balancer_by_lua_block - 在 Lua 中主动拉取各后端的实时 CPU 使用率(例如通过 HTTP API 或本地 /proc/stat),结合预设策略计算“有效权重”
- 按加权随机(Weighted Random)或最小负载(Least Load)逻辑,手动选择 target 并调用
set_current_peer()
获取后端 CPU 使用率的常见方式
需后端主动暴露指标,Nginx Lua 才能读取。推荐几种轻量方案:
-
HTTP 指标接口:每个后端提供
/health/cpu返回 JSON,如{"cpu_percent": 42.3};Lua 用resty.http异步并发请求 -
Prometheus 拉取:若已有 Prometheus,可查
node_cpu_seconds_total{mode!="idle"}计算最近 1 分钟使用率,但延迟略高 -
本地文件缓存:后端定时写入
/tmp/backend01.cpu,Lua 用io.open读取(注意权限与原子写入)
不建议在 Lua 中直接执行 os.execute("top -bn1") —— 开销大、不可靠、易阻塞 worker。
Lua 权重调度示例(加权随机)
假设已知三台后端及其当前 CPU 使用率,目标是 CPU 越低,被选中的概率越高:
balancer_by_lua_block {
local peers = {"192.168.1.10:8080", "192.168.1.11:8080", "192.168.1.12:8080"}
local cpu_data = {}
<pre class="brush:php;toolbar:false;">-- 此处应替换为实际采集逻辑(如 http.get)
cpu_data["192.168.1.10:8080"] = 25.1
cpu_data["192.168.1.11:8080"] = 63.7
cpu_data["192.168.1.12:8080"] = 12.4
-- 反向权重:CPU 越低,权重越高(避免除零)
local weights = {}
for _, addr in ipairs(peers) do
local cpu = cpu_data[addr] or 100
weights[#weights + 1] = math.max(1, 100 - cpu) -- 基础权重区间 1~100
end
-- 加权随机选择
local total = 0
for _, w in ipairs(weights) do total = total + w end
local rand = math.random() * total
local sum = 0
for i, w in ipairs(weights) do
sum = sum + w
if rand <= sum then
local host, port = string.match(peers[i], "(.+):(.+)")
ngx.var.upstream_host = host
ngx.var.upstream_port = port
balancer.set_current_peer(host, tonumber(port))
break
end
end}
注意事项与优化点
该方案实际落地需关注稳定性与性能:
- 采集 CPU 数据必须设置超时(
http:set_timeout()),失败时降级为轮询或使用历史缓存值 - 避免每次请求都全量采集——可用 shared_dict 缓存各后端 CPU 值(TTL 5~10 秒),减少网络开销
- 权重计算逻辑建议封装为独立函数,便于测试和复用
- 务必开启
lua_check_client_abort off,防止客户端中断导致 balancer 阶段异常
本质上,这不是“修改 weight”,而是用 Lua 构建了一套感知负载的动态路由层。只要采集及时、逻辑合理,效果远优于静态权重或简单 least_conn。


















