nginx njs无法直接修改upstream中server的weight,但可通过动态设置proxy_pass目标实现等效加权调度;利用os.loadavg()、/proc/meminfo或共享字典聚合p90响应时间生成实时调度决策。

Nginx 官方 NJS(NGINX JavaScript)模块本身不支持直接修改 upstream 块中 server 的 weight 参数。这是因为 upstream 配置在 Nginx 启动时编译固化,运行时不可变更;NJS 运行在请求处理阶段,无法触达底层 upstream 结构的权重字段。
但你可以用 NJS 实现效果等价的动态调度行为——不改 weight,而是通过 r.variables + proxy_pass http://$upstream_name 动态选择目标节点,结合实时系统指标(如 CPU、内存、连接数),达到“逻辑加权”的目的。关键在于:把权重决策前移到请求路由环节,绕过静态 upstream 权重限制。
用 NJS 获取系统负载并生成调度决策
NJS 可调用 os.loadavg()、os.sysctl('vm.loadavg')(Linux)、或读取 /proc/loadavg 文件获取当前负载均值。也可通过 require('fs').readFileSync() 读取 /proc/meminfo 计算内存使用率。
示例逻辑(放在 nginx.conf 的 http 块中):
// njs.conf 或内联 script
function selectBackend(r) {
const load = os.loadavg()[0]; // 1分钟平均负载
const cpuCount = os.cpus().length;
// 简单规则:负载越低,越倾向选该节点
const scores = {
'10.0.1.10:8080': 100 - Math.min(95, load * 10),
'10.0.1.11:8080': 100 - Math.min(95, load * 8),
'10.0.1.12:8080': 100 - Math.min(95, load * 12)
};
// 按得分降序取最高分节点(可扩展为加权随机)
const candidates = Object.entries(scores).sort((a, b) => b[1] - a[1]);
r.variables.upstream_target = candidates[0][0];
}
然后在 location 中使用:
location / {
js_content myModule.selectBackend;
proxy_pass http://$upstream_target;
proxy_set_header Host $host;
}
⚠️ 注意:proxy_pass http://$upstream_target 必须配合 resolver(若用域名)或确保 IP 直连可用;NJS 不会做健康检查,需自行加入探测逻辑(如缓存上次成功响应时间)。
结合后端真实响应时间做闭环反馈
仅看本机负载不够——节点可能 CPU 低但网络卡顿或 DB 拖慢。更可靠的方式是聚合历史响应时间:
- 在 access_log 中记录
$upstream_addr和$upstream_response_time - 用 NJS 定时器(
timer.setInterval)每 30 秒读取最近日志片段(或写入共享字典shared_dict),计算各节点 P90 响应时间 - 将
P90作为“反向权重”:score = 1 / (p90 + 10),再归一化为概率分布,用加权随机选择
这样就实现了:
✅ 不依赖外部脚本或 reload
✅ 每次请求都基于最新系统+业务指标决策
✅ 无需编译第三方模块(纯 NJS + OpenResty 或 Nginx 1.25.3+)
实际部署要点
- NJS 代码必须放在
http{}块中,用js_import引入或js_set/js_content内联 - 启用
js_set $upstream_target myModule.selectBackend;时,不能和proxy_pass在同一 location 共存;推荐用js_content - 避免在 NJS 中做阻塞操作(如同步 fs.readFile);优先用
shared_dict缓存指标,定时异步更新 - 为防单点误判,建议加入熔断:若某节点连续 3 次
upstream_status ~ "50[0-4]",临时将其 score 设为 0
不复杂但容易忽略。











