lua-resty-upstream-healthcheck需手动启动定时器探测、用shared_dict存状态,并在balancer_by_lua_block中主动过滤下线节点,否则不生效。

为什么 upstream 里加了 health_check 却不生效
因为 lua-resty-upstream-healthcheck 不是 Nginx 官方模块,也不在 upstream 块里通过 health_check 指令启用——那是 Nginx Plus 的功能。你装了 Lua 库,但没调用它,Nginx 就完全不知道你在探测什么。
真正生效的方式是:手动创建一个定时器(ngx.timer.at),在回调里用 checker:check() 主动轮询后端节点,并根据结果动态更新 shared_dict 中的节点状态。Nginx 的 upstream 本身不会自动读这个状态,你还得配合 balancer_by_lua* 阶段做路由决策。
- 必须在
init_worker_by_lua_block里启动健康检查器,否则 worker 进程间状态不共享或重复初始化 -
shared_dict要提前在http块声明,大小建议 ≥1m(存 IP+端口+状态+时间戳) - 检查器默认只标记“异常”,不自动剔除节点;剔除逻辑必须你自己在
balancer_by_lua_block里查字典、过滤掉 down 的节点
如何在 balancer_by_lua_block 里跳过已下线节点
这是最关键的路由控制点。Nginx 在这里把请求交给哪个后端,完全由你决定。不能依赖 upstream 的内置负载逻辑,得自己从 upstream 配置中提取所有 server,再逐个查健康状态。
示例逻辑片段(放在 location 或 upstream 对应的 balancer_by_lua_block 内):
local peers = require "resty.upstream.healthcheck".get_peers("my_backend")
local up_peers = {}
for _, p in ipairs(peers) do
local state = checker_dict:get("health:" .. p.host .. ":" .. p.port)
if state == "up" then table.insert(up_peers, p) end
end
if #up_peers == 0 then ngx.exit(503) end
local idx = ngx.ctx.balancer_index or 0
idx = idx % #up_peers + 1
ngx.ctx.balancer_index = idx
local peer = up_peers[idx]
balancer.set_current_peer(peer.host, peer.port)
-
checker_dict是你定义的shared_dict名,用来存健康状态,key 格式要和检查器写入的一致 - 务必加空列表兜底(
#up_peers == 0),否则所有请求会卡死或 fallback 到默认行为 - 如果用了 keepalive,注意
set_current_peer后连接池是否匹配;不匹配可能触发新建连接,影响性能
probe 接口返回 200 但节点仍被标为 down
常见原因是 probe 请求没走通,或者响应内容不符合检查器预期。默认配置下,lua-resty-upstream-healthcheck 不只看 HTTP 状态码,还会校验响应体是否包含指定字符串(rise_count/fall_count 是连续成功/失败次数,不是单次判定)。
- 检查
http块中checker:configure的port和uri:比如后端服务监听的是8080,但 probe 发到了80,必然超时 - 确认后端 probe 接口是否真返回了
body_pattern指定的内容(默认是空字符串,但若你设了"ok",而接口返回{"status":"ok"},就不匹配) - 用
tcpdump或strace -p $(pidof nginx)抓包验证 probe 是否发出、目标是否可达;有时候防火墙或 SELinux 会拦截 worker 进程的 outbound 连接 - 日志级别调到
debug,并在error_log中搜索healthcheck,能看到每次 probe 的耗时、状态码、body 截断内容
多个 worker 下状态不同步或反复震荡
根本原因是每个 worker 独立运行 timer,又各自维护一份 checker 实例,但 shared_dict 是共享的——所以问题不在存储,而在“谁来写”和“写什么”。如果多个 worker 同时 probe 同一节点,且判断节奏不一致,就容易出现 A 判 up、B 判 down 的情况。
- 必须用
init_worker_by_lua_block启动 checker,并确保只有一个 worker(比如用math.random() 加锁)执行 probe,其他 worker 只读状态 - 不要在
balancer_by_lua_block里调用checker:check(),那会导致每请求都 probe,彻底压垮后端 - 调整
interval(建议 ≥5s)和timeout(建议 ≤1s),避免 probe 积压;太短的间隔在高并发下极易误判 - 如果后端本身有抖动(如 GC 导致某次响应慢于 300ms),考虑把
fall_count设为 3~5,而不是默认的 2
主动探测这事,核心从来不是“能不能发请求”,而是“怎么让所有 worker 对同一节点的状态认知一致”,以及“怎么不让探测本身成为新的故障源”。细节都在 timer 控制、shared_dict 键设计、和 balancer 阶段的过滤逻辑里。


















