nginx 通过非阻塞 i/o 结合 epoll 等多路复用机制实现高并发:worker 调用 recv()/send() 遇阻塞立即返回 eagain,转而处理其他就绪连接;事件循环批量监听 socket 状态,单进程可高效管理数万连接,显著降低内存与上下文切换开销。

Nginx 中的 Non-blocking IO(非阻塞 I/O)是其支撑十万级并发连接的关键基础,它不靠堆线程或进程数量,而是让单个 worker 进程“忙而不闲”——在等待网络数据就绪时,立刻转向处理其他已就绪的请求。
非阻塞 I/O 的实际行为
当 worker 进程调用 recv() 或 send() 时,若内核缓冲区暂无数据可读、或无法立即写入,系统不会让该进程挂起等待,而是立刻返回错误码(如 EAGAIN 或 EWOULDBLOCK)。进程随即跳过当前连接,继续检查其他连接的 I/O 状态。
这种机制避免了传统阻塞模型中“一个连接卡住,整个线程停摆”的问题,使单个 worker 能持续轮询成千上万个 socket 描述符。
配合事件驱动与多路复用
Non-blocking IO 单独存在效果有限,Nginx 将其与操作系统级的 I/O 多路复用机制深度结合:
- Linux 下默认使用 epoll:支持边缘触发(ET)模式,仅在 socket 状态变化时通知,大幅减少无效唤醒
- 每个 worker 维护一个事件循环,统一监听所有已建立连接的读/写就绪事件
- 一次系统调用(如 epoll_wait)即可批量获取多个就绪事件,无需为每个连接单独轮询
资源开销显著降低
阻塞式模型下,10 万并发连接往往需要 10 万个线程——带来巨大内存占用(每个线程栈约 1–8 MB)和频繁上下文切换。而非阻塞 + 事件驱动模型下:
- worker 进程数通常设为 CPU 核心数(例如 8 个),每个进程处理数万连接
- 连接状态由少量结构体维护(如 ngx_connection_t),内存占用可控
- 无锁设计减少竞争,worker 间几乎不共享状态,避免同步开销
真实请求生命周期中的非阻塞体现
以一次静态文件请求为例:
- accept() 建立连接 → 设置 socket 为 non-blocking
- read() 尝试读取 HTTP 头 → 若未收全,立即返回 EAGAIN,记录该连接待读状态
- 转而处理其他已就绪连接;稍后 epoll 通知该连接可读,再继续解析
- open() 打开文件 → 使用 sendfile() 零拷贝发送 → 若 socket 发送缓冲区满,同样 EAGAIN,暂停发送,挂起写事件
- 等内核通知可写时再恢复发送,全程不阻塞主线程











