Nginx后端异步探测核心是复用单线程非阻塞事件循环,将探测任务注册为定时器与I/O事件,由epoll/kqueue统一调度,不新建线程、不阻塞worker,失败与恢复判断均在事件回调中完成,状态默认per-worker隔离。

Nginx 实现后端异步探测,核心不是靠“另起一套事件循环”,而是复用其固有的单线程、非阻塞、基于 epoll/kqueue 的事件驱动主循环。所有探测行为——无论是连接建立、超时判断,还是响应读取——都被注册为该循环中的普通事件,不创建新线程、不阻塞 worker,真正实现轻量、并发、零额外开销。
探测任务被当作定时器+I/O事件统一调度
每个 worker 进程在启动后就进入一个持续运行的事件循环。主动健康检查(如通过 nginx_upstream_check_module 或 lua-resty-upstream-healthcheck)会:
- 把下一次探测时间注册为一个定时器事件(例如每 3 秒触发一次)
- 到期后,发起一个非阻塞 TCP 连接或发送 HTTP 请求
- 将该 socket 加入当前 worker 的事件监听集合(如 epoll_wait 监听可写/可读/错误)
- 后续由同一事件循环在 socket 就绪或超时时回调处理,而非同步等待
这样,1 个 worker 可同时管理成百上千个探测任务,彼此互不干扰。
失败与恢复判断都在事件回调中完成
探测结果不依赖“等待响应”,而是在事件就绪时即时处理:
- 若 connect() 返回 EINPROGRESS,说明连接正在建立,事件循环继续监听可写事件
- 若 recv() 返回 0 或 -1 且 errno 是 ECONNRESET/ECONNREFUSED,立刻标记节点为 down
- 若收到完整 HTTP 响应,解析状态码和 body 后按
check_http_expect_alive规则判定是否健康 - 所有状态更新(up/down/checking)只作用于当前 worker 的内存副本,不跨进程
被动检查也走同样路径:请求转发失败(如 proxy_read_timeout 触发)时,错误处理逻辑直接在 recv() 回调中执行,同步更新该 server 的失败计数与时间戳。
状态隔离是默认行为,需显式解决共享问题
由于每个 worker 独立运行事件循环,它们对同一后端的状态判断可能不同:
- worker A 刚探测失败,标记 server 为 down
- worker B 尚未探测,仍认为它 up
- 这会导致流量分发不一致,尤其在低并发或刚启动时明显
缓解方式包括:
- 使用
shared memory zone(部分模块支持)让状态在 worker 间共享 - 依赖
max_fails/fail_timeout这类原生指令,因其统计逻辑虽 per-worker,但行为足够收敛 - 在 upstream 外加一层协调服务(如 consul + nginx-upsync-module),由外部统一决策
不需要额外线程或进程,也不需要修改内核,仅靠 Nginx 自身的事件模型就能支撑高密度、低延迟的后端探测。



















