init_worker_by_lua_block 是唯一安全的初始化位置,因每个 nginx worker 独立事件循环,checker 必须绑定其上下文发起异步探针;跨 worker 共享状态会导致错乱或静默失败。

为什么 init_worker_by_lua_block 是唯一安全的初始化位置
模块必须在 init_worker_by_lua_block 中启动 checker 实例,否则会出现状态错乱或静默失败。这是因为每个 Nginx worker 是独立事件循环,checker 需要绑定到具体 worker 的上下文才能发起异步探针;若在 init_by_lua_block 或 http 块里初始化,会尝试跨 worker 共享状态,而该模块不支持跨 worker 同步。
- 每个
upstream名称必须对应一个独立 checker 实例,不能复用同一段 Lua 代码初始化多个 upstream - 若 upstream 名为
api_v1,checker 初始化时传入的 name 参数也必须是"api_v1",否则状态无法映射到对应 peer - 未在
init_worker_by_lua_block中调用checker:start(),日志里不会报错,但/healthz返回空或全 down
probe 接口必须走独立管理端口,且不能被业务层中间件拦截
探测请求如果和业务流量共用 80/443 端口,极大概率被限流、鉴权、WAF 或慢日志模块干扰——比如某次探测因 JWT 过期返回 401,节点就被误判为不可用;又或者被 rate_limit.lua 拦截后超时,触发 fall 计数。
- 建议为后端服务额外暴露一个管理端口(如
8081),只挂载最简 handler(如直接返回200 OK) -
check_http_send发送的请求头应精简,避免带Authorization、X-Real-IP等业务相关字段 - 若后端是 Spring Boot,可用
management.endpoints.web.base-path=/actuator+endpoints.health.show-details=never配合反向代理暴露/actuator/health
shared_dict 缺失会导致 checker 启动失败但无明确错误提示
模块内部依赖 lua_shared_dict 存储每个 peer 的失败计数、最后探测时间、当前状态等运行时数据。如果配置中漏掉声明,checker 会在启动时 silently fallback 到不可靠的局部变量存储,导致状态不一致、fall/rise 计数失效、/healthz 输出混乱。
- 必须在
http块顶部显式声明:lua_shared_dict healthcheck 10m; - 10m 是保守值,每 100 个 upstream peer 约占用 200KB,可根据节点数线性估算
- dict 名称必须与 checker 初始化时传入的
shm参数一致,例如:checker:new({ shm = "healthcheck", ... })
超时值设得太大会拖垮真实请求的超时判定
探测本身是异步的,但它共享 Nginx 的连接池和超时上下文。如果 check_timeout 设为 15s,而你的 proxy_read_timeout 也是 15s,那么当某个后端响应缓慢时,探测请求会卡满整个 timeout 窗口,导致后续真实请求在进入 upstream 前就因连接池耗尽或事件循环阻塞而排队甚至超时。
-
check_timeout应明显小于proxy_read_timeout,推荐设为后者的 1/5~1/3,例如后者是 30s,则前者取5s -
check_keepalive必须启用(默认 true),否则每次探测都新建 TCP 连接,高频探测下可能触发TIME_WAIT爆炸 - 若后端健康接口本身有 GC 暂停或冷启动延迟,建议在 probe 脚本里加
sleep(0.1)模拟缓冲,而非拉长 timeout
hash $arg_uid 这类静态算法,Nginx 仍会把特定 uid 的请求持续打过去——必须搭配 least_conn 或 random 等支持 run-time peer selection 的算法,并确保没在 upstream 里硬写 down 标记。











