nginx事件驱动架构支撑数万并发的核心在于单worker进程+epoll实现io多路复用:epoll通过红黑树管理fd、仅返回就绪事件,避免select/poll的线性遍历开销;每个worker独享epoll实例并运行非阻塞事件循环;master-worker多进程模型则利用多核,worker数通常匹配cpu核心数。

Nginx 的事件驱动架构之所以能支撑数万并发连接,核心在于它用单个 worker 进程 + epoll 实现了高效的 IO 多路复用。这不是靠堆线程,而是靠内核主动通知、进程精准响应。
epoll 是如何替代 select/poll 的
select 和 poll 都需要每次调用时把全部文件描述符(fd)从用户态拷贝到内核态,并在线性结构中逐个检查状态。连接一多,开销就指数级上升。epoll 则完全不同:
- 首次调用 epoll_create 创建一个内核事件表,后续所有操作只操作这张表
- epoll_ctl 负责增删改监听的 fd 及关注事件(如 EPOLLIN、EPOLLOUT),内核内部用红黑树管理,插入/删除都是 O(log n)
- epoll_wait 阻塞等待时,内核只返回“真正就绪”的 fd 列表,无需遍历全部,时间复杂度接近 O(1)
Nginx worker 如何与 epoll 协同工作
每个 worker 进程启动后,会初始化自己的 epoll 实例,然后进入一个无限循环——这就是 Nginx 的事件循环主干:
- 调用 epoll_wait 等待就绪事件,超时时间由配置决定(如 timer_resolution 或 keepalive_timeout)
- 一旦返回,遍历就绪事件数组,对每个 fd 触发对应的回调函数:新连接走 accept 流程,已建立连接走 recv / send 流程
- 所有网络读写都设为非阻塞模式,避免单个慢请求卡住整个循环
- 定时器、信号、磁盘 I/O(如 sendfile)等事件也统一纳入 epoll 监控或通过事件队列协同
为什么必须搭配多进程模型
epoll 本身是单线程高效,但无法利用多核。Nginx 用 master-worker 多进程来补足这一点:
- master 进程不处理网络请求,只负责加载配置、fork worker、平滑重启
- 每个 worker 进程独立拥有自己的 epoll 实例和内存空间,互不干扰
- 新连接通过 accept_mutex 或 SO_REUSEPORT 机制分发给空闲 worker,避免惊群
- worker 数通常设为 CPU 核心数,配合 CPU 亲和性(worker_cpu_affinity),减少缓存失效
它仍是同步非阻塞 I/O
要注意:epoll 不等于异步 I/O。Nginx 的 read/write 操作依然在用户态完成,数据仍需从内核缓冲区拷贝到用户空间:
- 当 epoll_wait 返回可读事件,Nginx 调用 recv() 把数据从内核 socket 缓冲区读入自己的 buffer
- 如果 buffer 满或连接关闭,它会再次注册 EPOLLIN 继续监听;如果要发数据,则注册 EPOLLOUT
- 零拷贝优化(如 sendfile)可绕过用户空间,直接让内核把文件数据送入 socket 缓冲区,但仍是 epoll 驱动下的同步行为











