Nginx Event Loop 通过非阻塞I/O、事件通知与分阶段状态机规避阻塞:worker用epoll/kqueue监听就绪事件,socket设为non-blocking,HTTP请求拆解为可中断阶段,磁盘I/O优先用sendfile或aio,超时机制预防性控制连接生命周期。

Nginx 的 Event Loop 本身不处理 I/O 阻塞,而是从设计上规避阻塞发生——它不依赖“等待”,而是用非阻塞 + 事件通知 + 分阶段执行的组合策略,让 I/O 操作不再成为执行流的瓶颈。
Event Loop 不会等,只响应就绪事件
Nginx 每个 worker 进程运行一个独立的事件循环,底层调用 epoll(Linux)、kqueue(BSD/macOS)等系统机制。这些机制的特点是:
- 内核在 socket 数据到达、可写、出错等状态变化时,主动通知 Nginx
- worker 调用
epoll_wait()时,若无就绪事件,会挂起(休眠),不消耗 CPU;一旦有事件,立刻返回就绪列表 - 所有 socket 默认设为
non-blocking模式,read()或write()调用要么立即完成,要么返回EAGAIN/EWOULDBLOCK,绝不会卡住
这意味着:没有“阻塞等待”环节,也就没有传统意义上的 I/O 阻塞风险。
网络 I/O 全程非阻塞,靠状态机拆解请求
HTTP 请求被划分为多个可中断阶段(如读 header、读 body、转发 upstream、写响应),每个阶段只做少量工作:
- 收到请求头后,若 body 还没传完,就保存当前连接状态,注册
EPOLLIN等待后续数据 - 向 upstream 转发时,若后端还没响应,就设置
write_event_handler并返回事件循环,不等结果 - 响应发送中遇到缓冲区满(
EAGAIN),就临时注册EPOLLOUT,等 socket 可写再继续
整个过程没有线程挂起、没有栈保存、没有锁竞争,只是状态记录 + 事件注册 + 回调触发。
磁盘 I/O 是唯一可能阻塞的环节,但有明确规避路径
Nginx 默认读文件用 read(),这是同步阻塞操作。但它提供了两种主流缓解方式:
-
sendfile:内核直接把文件内容从磁盘 DMA 到 socket 发送缓冲区,完全绕过用户态内存拷贝,不触发阻塞读 -
aio(Linux):启用aio on;和directio后,使用内核原生异步文件读取,但需满足对齐、文件系统等限制
静态资源服务默认优先走 sendfile;需要内容过滤或动态生成时,才可能退回到阻塞读——此时可通过 limit_conn 控制并发数,避免单个慢请求拖垮整个 worker。
超时与连接管理进一步压缩阻塞窗口
Event Loop 内置红黑树定时器,对各类操作施加硬性时限:
-
client_header_timeout:header 读取超时,未完成则断连 -
client_body_timeout:body 上传超时,防止慢 POST 占用连接 -
send_timeout:响应发送超时,避免卡在 write() - 空闲连接自动关闭(
keepalive_timeout),释放 fd 资源
这些超时不是“检测阻塞后处理”,而是在事件注册时就设定好触发条件,属于预防性控制。
不复杂但容易忽略


















