Nginx 事件循环通过异步非阻塞 I/O 与多阶段处理天然支持全双工协议;依托 epoll/kqueue 双向监听、连接级上下文隔离、零拷贝帧转发及 HTTP/2 stream 级调度实现高效全双工通信。

Nginx 的事件循环(Event Loop)本身不直接“适配”全双工协议,而是通过异步非阻塞 I/O 模型 + 多阶段请求处理机制,天然支撑全双工通信场景(如 WebSocket、HTTP/2 流式响应、gRPC over HTTP/2)。关键不在改造 EventLoop,而在如何在其框架内正确组织读写逻辑、资源生命周期和状态同步。
基于 epoll/kqueue 的双向事件监听
Nginx 在 Linux 下默认使用 epoll(macOS 用 kqueue),可同时注册读(EPOLLIN)和写(EPOLLOUT)事件。对全双工连接(如升级后的 WebSocket 连接),Nginx 会为同一 socket 同时监听两端数据到达与发送缓冲就绪:
- 收到客户端帧(如 WebSocket ping 或业务消息)触发读事件,调用 handler 处理并可能立即生成响应帧
- 当发送缓冲区空闲(如上一帧已发出、TCP 窗口允许),内核通知 EPOLLOUT,Nginx 尝试推送待发帧
- 若一次未发完,Nginx 自动将连接保留在写就绪队列中,避免 busy-loop
连接状态与上下文隔离(per-connection context)
全双工要求每个连接独立维护收发状态(例如 WebSocket 的掩码密钥、帧序列、ping 计时器)。Nginx 通过 ngx_connection_t → data 指针挂载模块私有结构体(如 ngx_http_websocket_ctx_t),确保:
- 读回调和写回调共享同一上下文,可安全访问收发计数、缓冲区指针、心跳超时时间等
- 即使读写事件并发触发(如客户端连续发两帧,服务端同时要回 ping 和业务响应),状态不会错乱
- 连接关闭时,模块可统一清理收发缓冲、定时器、SSL session 等资源
零拷贝帧转发与流控协同
对于代理类全双工场景(如 Nginx 反向代理 WebSocket 到后端),需避免内存复制和写阻塞:
- 使用 ngx_chain_t 链表管理帧数据,支持 scatter-gather I/O,直接投递给 sendfile() 或 writev()
- 当上游写缓冲满时,暂停从下游读取(设置
c->read->handler = NULL),防止背压击穿 - 启用
proxy_buffering off+proxy_buffer_size 4k显式控制缓冲粒度,避免大帧阻塞小帧
HTTP/2 多路复用下的 EventLoop 扩展
HTTP/2 本身是应用层全双工协议,Nginx 通过 stream 级事件调度复用单个 TCP 连接:
- 每个 stream(请求/响应流)拥有独立的接收窗口、优先级和流控状态,但共享底层 connection 的 epoll 句柄
- EventLoop 不感知 stream,只驱动 frame 解析;HTTP/2 模块在解析 HEADERS/DATA/PUSH_PROMISE 帧后,分发到对应 stream 上下文
- 写操作按 stream 优先级入队,由 connection 层聚合为 SETTINGS/GOAWAY/CONTINUATION 帧发送,保持 TCP 连接高效利用










