Nginx 无法运行时修改 upstream 参数,但可通过 njs 在 proxy_pass 阶段动态选择后端地址,利用 shared_dict 存储实时权重与状态,结合健康检查实现无 reload 的动态路由。

Nginx 本身不支持运行时直接修改 upstream 的 weight、max_fails 等参数,但借助 njs(Nginx JavaScript 模块),可在不触发全局 reload 的前提下,实现对 upstream 行为的动态干预——关键在于“绕过配置重写”,转而用 njs 在请求处理阶段做实时决策。
利用 njs 实现请求级动态路由
njs 不能改写 upstream 块定义,但可以在 proxy_pass 阶段动态选择后端地址,等效于“软权重切换”。核心思路是:把后端列表从静态配置移到 njs 变量或外部存储(如 shared dict / Redis),再通过脚本逻辑决定本次请求发给谁。
- 在
http块中定义一个 njs 函数,返回目标 server 地址(如"10.0.1.10:8080"或带协议的完整 URL) - 该函数可读取共享内存(
shared_dict)中的实时指标(错误率、延迟、自定义标记) - 结合时间、健康状态、连接数等条件,按规则返回不同后端,例如:
– 错误率 > 3% → 返回备用节点
– CPU > 85% → 返回 weight=1 的降级节点
– 夜间时段 → 主动切到低配实例 - 在 location 中使用
proxy_pass $backend;,其中$backend是 njs 函数的返回值
用 shared_dict 存储可变权重与状态
njs 提供 shared_dict 对象,支持跨 worker 进程读写键值对,适合存放各后端的实时权重、故障计数、最后健康检查时间等数据。
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 初始化:在 njs 文件中声明
var dict = require('dict').shared_dict('upstream_state'); - 更新:由外部监控脚本(如 Prometheus Alertmanager 调用 webhook)或定时任务,通过
ngx.fetch或本地 HTTP 接口写入新权重(如dict.set('backend-a:weight', '2')) - 读取:njs 路由函数每次调用时读取
dict.get('backend-a:weight'),参与加权轮询计算 - 注意:shared_dict 不支持复杂结构,建议扁平化键名,如
backend-a:error_rate、backend-b:last_ok
避免 Master 进程 reload 的本质逻辑
传统 reload 是因为 upstream 结构变更需重新解析配置树;而 njs 方式全程不碰 upstream { ... } 块,所有“配置”都以变量或共享内存形式存在,仅影响请求分发逻辑。
- upstream 块保持静态,只定义基础连接参数(
max_conns、fail_timeout等) - njs 函数替代了
proxy_pass http://backend中的固定 upstream 名,变成运行时计算值 - 即使 shared_dict 中的值每秒更新多次,Nginx worker 也无需 reload,无连接中断风险
- 若需新增/删除后端节点,仍需 reload——但这种情况远少于权重调整
配合健康检查提升自动性
njs 本身不提供主动探活,需结合 Nginx 原生健康检查(health_check 指令)或外部探测服务,再将结果同步进 shared_dict。
- 启用 upstream 内置健康检查:
health_check interval=5 fails=2 passes=3; - 用 njs 定时器(
setTimeout)或外部脚本定期调用ngx.status获取各 server 的状态(upstream.fails、upstream.down) - 将状态写入 shared_dict,供路由函数判断是否跳过该节点
- 例如:若
dict.get('backend-c:down') === '1',则当前请求自动避开 backend-c

















