Consul + NJS 实现 Upstream 动态扩缩容,核心是绕过 Nginx reload:通过 Consul 健康发现与 NJS 内存级 shared_dict 管理节点,proxy_pass 动态指向 $backend_url,零配置侵入、连接不中断、扩容缩容无感。

Consul + NJS 实现 Upstream 动态扩缩容,核心在于绕过传统 Nginx reload 机制——不重启 worker,不中断连接,不触发 master 进程重载,真正实现配置热更新。
Consul 作为服务发现中枢,实时同步实例状态
将后端服务(如 API 节点)注册到 Consul,启用健康检查(HTTP/TCP/TTL)。NJS 通过 Consul 的 /v1/health/service/{service} 接口轮询获取健康节点列表。关键点:只拉取 Passing 状态的实例,自动剔除故障节点,无需人工干预或配置下发。
- 建议设置合理轮询间隔(如 5s),避免 Consul 压力过大,也防止延迟过高
- 利用 Consul 的 blocking query(长轮询)可进一步降低延迟,NJS 中用
ngx.fetch配合timeout控制阻塞时长 - 服务名、数据中心、Token(如需 ACL)应通过 NJS 模块配置项注入,而非硬编码
NJS 模块维护内存级 Upstream 映射表
NJS 不支持直接修改 Nginx 内置 upstream 块,但可通过 shared dictionary(如 shared_dict upstream_cache 10m;)持久化节点列表,并在 js_set 或 js_content 中动态计算目标地址。真实请求由 proxy_pass 指向变量(如 proxy_pass $backend_url;),而该变量由 NJS 函数实时生成。
- 每个请求前调用 NJS 函数,从 shared_dict 读取最新节点数组,按权重/轮询/一致性哈希逻辑选一个 IP:Port
- 节点变更时,仅更新 shared_dict 中的 JSON 字符串(如
dict.set("nodes", JSON.stringify(nodes))),无锁、无重启 - 为防字典键冲突或过期,建议加版本号或时间戳字段,NJS 函数内做简单校验
规避 reload 的关键设计:零配置文件侵入
整个流程不依赖 upstream {} 块声明,不修改 nginx.conf,不触发 nginx -s reload。所有节点信息存在内存(shared_dict)与外部服务(Consul)中,Nginx worker 进程始终运行,连接复用、SSL session 复用、HTTP/2 stream 全部保持稳定。
- 若需新增服务类型,只需在 Consul 注册新 service 名,并在对应 NJS 函数中增加路由分支,无需改 Nginx 配置
- 扩容即服务注册,缩容即服务注销或健康检查失败——Nginx 侧完全无感
- 极端情况(如 shared_dict 损坏)可 fallback 到内置静态列表,保障基本可用性
可观测性与兜底策略不可少
动态 Upstream 失去 reload 的“强制校验”后,必须补足运行时监控。NJS 可暴露 Prometheus 格式指标(如节点数、请求分发成功率、Consul 请求延迟),并通过 log_by_js 记录异常选节点行为;同时配置 proxy_next_upstream 和 proxy_next_upstream_tries,当 NJS 返回的地址不可达时,自动尝试备选节点或返回错误,而非直接 502。
- 定期用
curl http://localhost:8080/nginx_status(配合 stub_status)验证 worker 连接数是否平稳,确认无 reload 导致的抖动 - 在 NJS 中加入熔断逻辑:连续 N 次 Consul 请求失败,则降级使用上一次缓存结果,并告警
- Consul 客户端超时设为


















