Nginx的异步非阻塞能力由多进程+单线程事件循环+epoll多路复用协同实现:worker进程初始化时创建epoll实例并注册事件,通过epoll_wait驱动事件循环,结合非阻塞I/O分段处理连接,使单worker可高效并发处理成千上万请求。

Nginx 的异步非阻塞能力不是靠单一线程或协程实现的,而是由多进程 + 单线程事件循环 + epoll 多路复用三层结构协同完成。它不等待 I/O 完成,也不为每个连接分配独立执行流,而是在少量 worker 进程内,把成千上万个连接的状态变化统一纳入一个高效调度体系。
worker 进程如何启动事件循环
每个 worker 进程在初始化时调用 ngx_event_process_init,完成三件事:创建 epoll 实例(epoll_create)、注册监听套接字的读事件(EPOLLIN)、将所有待监控 socket 加入红黑树管理。此时事件循环尚未开始,只是准备好“监听名单”。
- 监听套接字被设为 non-blocking 模式,accept 调用不会卡住进程
- 每个连接对应的
ngx_connection_t结构体绑定读/写事件对象,并与 epoll 关联 - 定时器事件(如超时控制、健康检查)被组织进红黑树,用于计算
epoll_wait的最大等待时间
epoll_wait 如何驱动整个流程
ngx_process_events_and_timers 是事件循环的核心函数,每次调用都执行以下动作:
- 先检查 accept 互斥锁(避免惊群),有权限则尝试接收新连接
- 调用
epoll_wait等待就绪事件,超时时间取自定时器红黑树最小值 - 若返回就绪事件,遍历
rdllist链表,分发给对应连接的读/写 handler - 若无就绪事件但定时器到期,则触发超时逻辑(如关闭空闲连接、重试 upstream)
关键点在于:epoll_wait 只在所有连接都无事可做且无定时器到期时才真正阻塞;其余时间它快速返回,让 Nginx 持续处理已就绪的 I/O。
非阻塞 I/O 在数据收发中如何落地
当某个连接触发 EPOLLIN 事件,Nginx 调用 recv 读取数据:
- 如果内核缓冲区有数据,立即读完并继续处理请求体或响应生成
- 如果返回
EAGAIN或EWOULDBLOCK,说明暂时没数据——不等待,直接退出当前读流程 - 该连接的读事件保持注册状态,下次 epoll_wait 再就绪时继续读,形成“分段续读”
- 写操作同理:缓冲区满时写不全,就注册 EPOLLOUT,等可写再发剩余部分
这种机制使单个 worker 能交错处理多个连接的不同时期(如 A 正在传请求头、B 等待后端响应、C 正在发静态文件),没有一处因 I/O 停摆。
异步性体现在业务处理层面而非系统调用本身
需要明确的是:epoll 本身是同步接口,epoll_wait 会阻塞;但 Nginx 的“异步”是指对用户请求的处理逻辑不依赖某次 read/write 的完成。
- 接收请求体时,只读到部分数据就暂停,后续由事件回调驱动继续
- 转发请求到 upstream 时,不等后端返回,先去处理其他连接,等 upstream socket 就绪再读响应
- gzip 压缩、SSL 握手等耗时操作,在 1.7.11+ 版本中可交由 thread pool 异步执行,避免阻塞事件循环
这种设计让每个 worker 始终处于“有事做事、无事等事”的高效状态,既规避了线程切换开销,又避免了阻塞式模型的资源浪费。


















