Nginx利用EventLoop实现单线程处理万级连接原理

1次阅读

nginx采用线程+多worker进程模型,每个worker为单线程事件循环,基于epoll/kqueue非阻塞I/O、零拷贝sendfile及轻量内存处理,实现万级并发连接。

Nginx利用EventLoop实现单线程处理万级连接原理

Nginx 并非真正“单线程”,而是采用一个主线程 + 多个工作进程(worker process)的模型,每个 worker 进程内部使用事件驱动 + 非阻塞 I/O + Epoll(linux)或 Kqueue(BSD/macos)等高效事件通知机制,实现单个进程处理数万并发连接。核心不在于“单线程”,而在于每个 worker 是单线程、无锁、事件循环驱动的高效状态机

EventLoop 是什么:不是线程,是调度逻辑

在 Nginx 中,EventLoop 指的是每个 worker 进程内持续运行的事件循环主循环(典型结构为 for(;;) { epoll_wait(); 处理就绪事件; } )。它不创建新线程,也不为每个连接分配独立或上下文,而是:

  • 把所有客户端连接抽象为“文件描述符(fd)”,注册到内核事件表(如 epoll)
  • 一次系统调用(epoll_wait)批量获取当前就绪的读/写事件(比如 1000 个连接同时发来请求,可能只返回几十个活跃 fd)
  • 按需顺序处理这些就绪事件——接收、解析 http、读取静态文件、转发给上游、构造响应、发送回包,全程不阻塞
  • 所有操作基于回调式状态转移(如:收到 header → 等待 body → 构造响应 → 发送 header → 发送 body → 关闭或 keepalive)

为什么能扛住万级连接:三重非阻塞协同

高并发能力来自三个层面的非阻塞设计叠加:

  • 网络 I/O 非阻塞:socket 设为 non-blocking,recv/send 不会卡住;即使暂时无数据或缓冲区满,也立即返回 EAGAIN/EWOULDBLOCK,让出 CPU 给其他事件
  • 磁盘 I/O 尽量规避:静态文件通过 sendfile() 零拷贝直接由内核从磁盘送入 socket 缓冲区;大文件或需要处理的内容才用异步读(AIO),避免 worker 被阻塞
  • 逻辑处理轻量且无等待:Nginx 的 HTTP 解析、路由、header 修改等全部在内存中完成,无同步锁、无复杂对象生命周期管理、无 GC 停顿(C 语言实现)

连接数 ≠ 并发请求数:连接复用与状态分离

万级连接不等于万级同时处理的请求。Nginx 巧妙利用 HTTP 特性降低压力:

  • HTTP/1.1 默认 keepalive:一个 TCP 连接可承载多个请求-响应周期,连接长期存在但大部分时间处于“空闲等待”状态
  • 空闲连接几乎不消耗 CPU:epoll 不会通知它,worker 只在有数据到达或超时到期时才处理它
  • 每个连接只占用约 2–4 KB 内存(socket 结构 + request context),远低于传统每连接一线程模型(几 MB/线程)
  • 超时机制自动回收僵尸连接(keepalive_timeout、client_header_timeout 等)

不是靠“单线程”胜出,而是靠“无共享 + 事件驱动 + 内核协作”

很多人误以为“单线程所以快”,其实关键在于:

  • worker 进程间无共享内存,无需锁和同步,彻底规避多线程竞争开销
  • 所有事件都由内核统一通知(epoll),避免轮询浪费 CPU
  • 整个处理流程是纯函数式状态推进,每个请求的上下文(ngx_http_request_t)独立分配、明确释放,没有隐式依赖
  • 配置热加载、平滑重启等机制都建立在此模型之上,不影响已有连接处理

本质上,Nginx 把“高并发”问题转化成了“高效事件分发 + 快速状态切换 + 最小资源驻留”的工程实践,EventLoop 是这个闭环的执行中枢,不是玄学,而是 C + Linux + HTTP 协议深度协同的结果。

text=ZqhQzanResources