Nginx无法在upstream块内直接执行Lua,需用balancer_by_lua_block替代静态配置,结合shared dict实现动态健康检查、主动探测、故障剔除与指数退避恢复。

Nginx 本身不支持在 upstream 阶段直接执行 Lua 代码(比如 balancer_by_lua* 是在负载均衡阶段,而非 upstream 定义阶段),但可以通过 lua-upstream-nginx-module(OpenResty 内置)结合 balancer_by_lua_block 实现动态健康检查与后端剔除——关键在于把“健康状态管理”从静态配置移到运行时 Lua 中,由 Lua 控制哪些节点可用、何时踢出、何时恢复。
用 balancer_by_lua_block 替代静态 upstream
放弃传统 upstream {...} 块,改用 set $backend '' 占位,再在 location 中用 balancer_by_lua_block 手动选节点。此时 Lua 完全掌控后端列表、健康状态、权重、超时等逻辑:
- 维护一个全局共享字典(shared dict)存储每个 backend 的 last_check、fail_count、down 状态
- 每次请求进来,遍历可用节点,跳过标记为 down 的,按权重或轮询选出目标
- 选中后调用
balancer.set_current_peer(ip, port)显式设置目标
实现轻量级主动健康检查(无需外部 probe)
不依赖 health_check 指令(它只支持 passive 或基于 tcp/http 的定时 probe),而是用 Lua 定时协程(ngx.timer.every)做主动探测:
- 在 init_worker_by_lua_block 中启动一个 timer,每 5 秒遍历所有 backend 发起 HEAD 请求
- 成功则重置
fail_count = 0,标记down = false;失败则fail_count++ - 当
fail_count >= 3,设down = true并记录last_down_time - 可加入“冷静期”:down 后 60 秒内不参与调度,之后自动试探性恢复
被动故障感知 + 自动剔除(更实用)
在 balancer_by_lua_block 中选节点后,若后续 upstream 返回 502/503/504 或超时,可通过 log_by_lua_block 捕获错误并更新健康状态:
-
log_by_lua_block中检查ngx.var.upstream_status和ngx.var.upstream_response_time - 若状态码异常或响应时间 > 3s,对当前 peer 执行
shdict:incr('fail_count:'..peer_key, 1) - 同时触发一次快速重检(如发起一个非阻塞 HTTP 探测),确认是否真宕机
- 确认后写入
down:ip:port = 1,并在下次 balancer 阶段跳过该节点
恢复机制:避免永久剔除
单纯踢出容易导致“雪崩后无法自愈”,需设计恢复逻辑:
- 为每个 down 节点记录
down_since时间戳(用 shared dict 存) - 在 balancer 阶段,若发现某节点 down 且已过 30 秒,尝试发一个轻量 probe(如 /healthz)
- probe 成功则清空 fail_count、置 down = false;失败则延长 down 时间(如翻倍)
- 可配合指数退避 + 最大 down 时长(如 5 分钟),防止长期隔离正常节点
本质上不是“在 upstream 阶段用 Lua”,而是绕过 upstream 配置,用 Lua 全权模拟 upstream 行为——OpenResty 提供的 balancer.* API 和 shared dict 是核心支撑。不复杂但容易忽略 timer 生命周期和 shared dict 并发安全,务必用 shdict:lock/unlock 或原子操作保护状态更新。


















