Nginx通过将非阻塞I/O与epoll/kqueue等内核事件通知机制结合,构建事件驱动循环实现高并发:worker注册所有socket,内核就绪时主动通知;单次事件可批量处理多个连接,配合accept_mutex避免争抢;HTTP请求被拆解为异步阶段,全程非阻塞,天然规避资源争抢与假死。

非阻塞 I/O 本身不直接“分发”事件,它只是让每次读写操作不卡住进程;真正实现高并发请求高效调度的,是 Nginx 将非阻塞 I/O 和操作系统事件通知机制(如 epoll 或 kqueue)深度绑定后形成的事件驱动循环。
事件分发依赖操作系统内核通知
Nginx 启动时,每个 worker 进程会把所有监听 socket 和已建立的 client/upstream socket 全部注册进 epoll(Linux)或 kqueue(BSD/macOS)。这些系统调用不是轮询,而是由内核在数据就绪、连接可接受、缓冲区可写等时刻主动通知 Nginx。
- 一个 socket 收到新数据 → 内核标记为“EPOLLIN 就绪” → epoll_wait() 返回该 fd
- 向客户端发送响应时 TCP 缓冲区满 → send() 返回 EAGAIN → Nginx 注册 EPOLLOUT 事件,等内核通知“可写了”再续发
- 上游响应返回 → 对应 upstream socket 触发 EPOLLIN → 事件循环立即调度 proxy_read 阶段
单次事件触发可批量处理多个就绪连接
默认情况下,Nginx 每次只处理一个就绪事件。但启用 multi_accept on; 后,一次 epoll_wait() 返回多个就绪连接,worker 可连续 accept 多个新连接、或集中处理一批可读请求,显著减少系统调用次数和上下文切换开销。
- 尤其在突发流量涌入时,避免大量连接堆积在 listen queue 中等待被逐个唤醒
- 配合 accept_mutex on;(默认开启),防止多个 worker 同时争抢 accept,降低锁竞争
连接生命周期被拆解为异步事件阶段
HTTP 请求不再是一条线同步执行到底,而是被事件循环按需调度各个子阶段:
- TCP 握手完成 → 触发 accept 事件 → 分配内存、设置非阻塞标志、注册读事件
- 客户端慢发 header → 每次 recv() 返回 EAGAIN → 继续监听 EPOLLIN,不阻塞也不丢弃
- 转发请求给后端 → 当前连接挂起,worker 立即转向其他就绪 fd
- 后端响应到达 → EPOLLIN 触发 → 解析响应头、检查缓存策略、准备发送
- send() 遇阻 → 记录已发字节数,注册 EPOLLOUT → 下次通知再续传
事件调度天然规避资源争抢与假死
因为所有 I/O 操作都设为非阻塞,且只响应内核通知,整个 worker 进程不会因任一环节停滞:
- 某个客户端网络抖动或 TLS 握手慢 → 对应 socket 一直无 EPOLLIN,不影响其他连接处理
- 上游服务延迟 5 秒 → Nginx 不等待,而是把该连接挂回 epoll,继续服务其余数千连接
- 日志磁盘写满或临时目录不可写 → 错误被捕获并记录,不会导致 worker 进入不可中断睡眠


















