nginx原生不支持upstream中直接使用环境变量设置weight,因weight需编译期确定;可行方案包括:①njs动态选节点、②openresty+lua实现加权调度、③外部api配合动态服务器模块。

Nginx 原生不支持在 upstream 块中直接引用环境变量(如 $ENV_VAR)来设置 weight,因为 upstream 指令在配置解析阶段就完成编译,此时变量尚未求值,且 weight 必须是编译期确定的整数常量。所以不能写成 server 10.0.1.10:8080 weight=$MY_WEIGHT;——这会导致 nginx -t 报错或被忽略。
但你可以通过“绕过静态 weight”的方式,实现效果等价的动态权重控制:把权重决策逻辑移到请求处理阶段,用运行时变量决定实际转发目标。核心思路是——不改 weight,改 proxy_pass 目标。
✅ 利用 NJS(NGINX JavaScript)读取环境变量并动态选节点
NJS 可以访问系统环境变量(通过 os.getenv()),结合 r.variables 和 proxy_pass http://$upstream_target 实现逻辑加权。
示例配置(nginx.conf 中):
# 加载 NJS 脚本(可内联或 external)
js_import /etc/nginx/njs/weight_router.js;
upstream backend_pool {
# 这里不设 weight,仅作占位或用于 fallback
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
listen 80;
location / {
js_content weight_router.selectByEnv;
proxy_pass http://$upstream_target;
proxy_set_header Host $host;
}
}
/etc/nginx/njs/weight_router.js 内容:
function selectByEnv(r) {
// 读取环境变量(需确保 nginx 启动时已导出,如 export NODE_A_WEIGHT=7)
const wA = parseInt(os.getenv('NODE_A_WEIGHT') || '5');
const wB = parseInt(os.getenv('NODE_B_WEIGHT') || '3');
const wC = parseInt(os.getenv('NODE_C_WEIGHT') || '2');
// 构建带权重的候选池(可扩展为加权随机轮询)
const candidates = [
{ addr: '10.0.1.10:8080', score: wA },
{ addr: '10.0.1.11:8080', score: wB },
{ addr: '10.0.1.12:8080', score: wC }
];
// 简单按 score 降序取最高(或实现 weighted random)
candidates.sort((a, b) => b.score - a.score);
r.variables.upstream_target = candidates[0].addr;
}
export default { selectByEnv };
⚠️ 注意:
- 环境变量需在 启动 nginx 主进程前导出(如 systemd service 文件中加
Environment="NODE_A_WEIGHT=8"),NJS 才能读到;proxy_pass http://$upstream_target要求目标是 IP:PORT 或已配置的 resolver 域名;- 此方案无健康检查,建议在脚本中加入
try/catch+ 缓存上次成功节点,或搭配max_fails/fail_timeout使用。
✅ 利用 OpenResty + Lua(更成熟、支持共享状态)
如果你用的是 OpenResty,可用 ngx.var 或 ngx.getenv() 获取环境变量,并在 balancer_by_lua_block 中做实时调度:
upstream backend {
server 10.0.1.10:8080; # 占位,实际不靠它轮询
server 10.0.1.11:8080;
server 10.0.1.12:8080;
balancer_by_lua_block {
local env_a = tonumber(ngx.getenv("NODE_A_WEIGHT")) or 5
local env_b = tonumber(ngx.getenv("NODE_B_WEIGHT")) or 3
local env_c = tonumber(ngx.getenv("NODE_C_WEIGHT")) or 2
local weights = {
["10.0.1.10:8080"] = env_a,
["10.0.1.11:8080"] = env_b,
["10.0.1.12:8080"] = env_c,
}
-- 加权随机选择(简单版)
local total = env_a + env_b + env_c
local rand = math.random() * total
local acc = 0
for addr, w in pairs(weights) do
acc = acc + w
if rand <blockquote><p>✅ 优势:支持熔断、连接数限制、共享字典缓存指标;<br>
❗ 要求 OpenResty,且需启用 <code>lua_shared_dict</code> 配合更复杂的闭环调控。</p></blockquote><hr><h3>✅ 替代方案:用外部 API + nginx-upstream-dynamic-servers 模块</h3><p>如果只是想“让权重随环境变量变化”,更推荐用脚本监听变量变更,再调用模块提供的 HTTP 接口更新:</p><pre class="brush:bash;toolbar:false;"># 示例:当 NODE_A_WEIGHT 改变时,推送新权重
curl -X POST http://localhost/upstream/servers \
-d '{"server":"10.0.1.10:8080","weight":'"$NODE_A_WEIGHT"' }'配合 systemd 的 EnvironmentFile= 或 conf.d 下的 .env 文件,由 watchdog 脚本触发更新——比纯 NJS 更稳定、可观测。
不支持直接变量赋值,但所有可行路径都围绕一个事实:权重逻辑必须移出 upstream 块,在请求级或控制面实现。选哪种,取决于你是否已有 OpenResty、是否接受 reload、以及对实时性的要求。











