Nginx的异步事件驱动模型依托epoll实现高并发:worker进程创建epoll实例并注册监听套接字,内核通过红黑树与回调机制高效管理就绪事件;epoll_wait动态设置超时以兼顾I/O与定时器;所有socket设为非阻塞,配合分段读写与状态机驱动,使单worker可并发处理数万连接。

Nginx 的异步事件驱动模型之所以能支撑数万并发,核心就在 Linux 下的 epoll——它不是简单的“轮询”,而是一套由内核深度配合的高效通知机制。
epoll 怎么注册监听对象
每个 worker 进程启动时,会调用 epoll_create() 创建一个 epoll 实例(本质是内核中一个 eventpoll 结构体)。这个实例生命周期贯穿整个 worker 运行期,只创建一次。
接着,Nginx 把监听套接字(比如 0.0.0.0:80 对应的 fd)用 epoll_ctl(EPOLL_CTL_ADD) 加入该实例,并关注 EPOLLIN 事件。
这个操作会让内核把该 fd 插入 epoll 内部的红黑树,并与网卡驱动建立回调关联:一旦有新连接到达,网卡中断触发后,内核直接调用 ep_poll_callback(),把就绪事件塞进 epoll 的就绪链表(rdllist),不依赖用户态轮询。
epoll_wait 如何做到“几乎不空等”
worker 进入事件循环后,反复调用 epoll_wait()。但它不是无脑阻塞:
- 超时时间并非固定值,而是动态计算——取所有定时器(如 keepalive 超时、resolver 超时)中最近到期的时间点
- 如果有就绪事件,
epoll_wait立即返回,遍历 rdllist 处理(如 accept 新连接、recv 请求头) - 如果没有就绪事件,但定时器快到期了,它仍会快速返回,以便执行超时逻辑(例如关闭空闲连接)
- 只有真正“既无事件也无定时器”时,才会进入轻量级休眠,CPU 占用趋近于零
非阻塞 I/O 和事件状态如何衔接
所有 socket 都设为 O_NONBLOCK 模式。当某个连接触发 EPOLLIN:
- Nginx 调用
recv()尝试读数据 - 若返回
EAGAIN或EWOULDBLOCK,说明当前缓冲区已空,本次读结束,但该连接的读事件保持注册状态 - 下次
epoll_wait再就绪时继续读,形成“分段续读” - 同样,发送时若
send()不能全发完,就注册EPOLLOUT,等可写再发剩余部分
这种设计让单个 worker 不会卡在任何一次系统调用上,同一时刻可交错推进成百上千个连接的不同阶段。
事件与连接、请求的状态绑定
每个活跃连接对应一个 ngx_connection_t 结构体,其中绑定了读/写事件对象(ngx_event_t),并和 epoll 关联。
事件处理函数(如 ngx_http_wait_request_handler)不负责完整请求流程,只做当前状态允许的操作(比如只收请求行),然后交由 HTTP 状态机驱动下一步。整个生命周期靠事件触发 + 状态迁移推进,而非线性阻塞执行。



















