nginx event loop 不直接管理连接状态,而是通过 epoll/kqueue 监听 fd 的 epollin/epollout/epollerr 事件,由 handler 在事件触发后解析协议、推进状态;超时与共享状态分别依赖 timer wheel 和 shared memory 等机制协同。

Nginx 的 Event Loop 不直接“处理”连接状态,而是通过事件驱动机制,把状态变化转化为可监听、可调度的 I/O 事件,由内核通知 + 预设回调协同完成状态管理。
它不维护协议层面的状态机(比如 HTTP 请求是否完整、WebSocket 是否握手成功),而是依赖文件描述符(fd)的就绪状态——可读、可写、出错——来触发对应 handler,再由 handler 根据当前连接上下文做具体判断和推进。
连接状态靠 fd 就绪事件间接反映
每个活跃连接在内核中对应一个 socket fd,Event Loop 把它注册进 epoll/kqueue 等多路复用器,持续监听三类基础事件:
- EPOLLIN / EV_READ:表示该 fd 有数据可读(新连接到达、HTTP 请求头/体到达、WebSocket 帧到达、心跳包到达等)
- EPOLLOUT / EV_WRITE:表示该 fd 发送缓冲区空闲,可以安全写入(响应数据、升级响应、ping 帧等)
- EPOLLERR / EV_ERROR:表示连接异常(对端关闭、RST、超时等),需立即清理
这些事件不是“连接状态”的抽象描述,而是操作系统内核反馈的底层 I/O 能力信号。Nginx 只信这个,其余都交给模块逻辑。
状态演进由 handler 在事件触发后完成
当某个 fd 就绪,Event Loop 调用其绑定的 handler(如 ngx_http_init_request 或 ngx_event_accept),该 handler 才真正解读当前连接所处阶段:
- 新连接到来 →
ngx_event_accept调用accept(),分配ngx_connection_t,设置read->handler = ngx_http_init_request - 第一次可读 →
ngx_http_init_request解析请求行和头部,判断是否为 WebSocket 升级,决定后续走 HTTP 还是 WebSocket 流程 - 已建立长连接再次可读 →
ngx_http_websocket_read_handler解析帧,检查 ping/pong/文本帧,更新连接活跃时间 - 可写但上次 send 未完成 →
ngx_http_websocket_write_handler尝试发送 output buffer 中剩余数据,失败则重注册 EPOLLOUT
也就是说,连接状态的“变化”发生在 handler 执行过程中,Event Loop 只负责准时唤醒它。
多连接共存靠事件队列与非阻塞 I/O 保障
- 所有连接共享同一个 epoll 实例,Event Loop 每次
epoll_wait()返回一批就绪 fd,逐个处理,不阻塞其他连接 - 每个 handler 必须使用非阻塞 socket:
recv()只取当前可用字节,send()失败时暂存数据并等待 EPOLLOUT,避免卡住整个循环 - 连接超时由定时器(timer wheel)配合事件触发:比如
client_header_timeout是在ngx_http_init_request启动一个定时器,若规定时间内未收完 header,则主动关闭连接
共享状态需额外机制支撑
Event Loop 本身不跨 worker 共享数据,但连接相关元信息(如会话活跃时间、版本水位、健康标记)可通过以下方式协同:
-
shared memory zone(如zone upstream_zone 128M)供所有 worker 读写心跳时间或冲突窗口标识 -
ngx_http_limit_conn_module或 OpenResty 的shared_dict提供毫秒级内存访问,用于拦截越界操作(如 version=1001 但全局已到 1005) - upstream 动态健康检查(如
nginx_upstream_check_module)每 100ms 主动探测后端,结果实时更新 upstream 状态,影响后续路由决策
不复杂但容易忽略。











