nginx event loop 不直接处理写操作,而是通过非阻塞i/o、事件注册与状态挂起将写操作转为异步任务,分“尝试→挂起→唤醒→继续”四步推进;共享写事件队列,支持数万连接并发写;结合sendfile零拷贝与反向代理读写解耦,实现高并发可伸缩写调度。

Nginx 中 Event Loop 并不直接“处理”写操作,而是通过非阻塞 I/O + 事件注册 + 状态挂起的方式,把写操作变成可中断、可重入、可调度的异步任务。它不追求一次写完,而是在内核发送缓冲区有空闲时才推进,从而让单个 worker 进程能同时管理成千上万个连接的读写状态。
写操作被拆解为“尝试→挂起→唤醒→继续”四步
当需要向客户端发送响应(如 HTML、图片或代理返回的数据)时,Nginx 不会阻塞等待全部数据发完,而是:
- 调用 非阻塞 send(),只发送当前内核 socket 发送缓冲区允许的字节数;
- 若缓冲区已满(返回 EAGAIN/EWOULDBLOCK),立即停止,不等待;
- 把剩余未发送的数据暂存到该连接的 output buffer 中;
- 向 epoll 注册 EPOLLOUT 事件,等待内核通知“可写”;
- Event Loop 下次循环中收到可写事件,再继续发送剩余数据,直到清空 buffer 或再次阻塞。
多个连接共享一个写事件队列,不排队不阻塞
Event Loop 每次调用 epoll_wait() 可能同时返回几十甚至上百个“可写”的连接 fd。它按顺序逐个处理,但每个连接只做一次小批量写(避免长时间占用 CPU),写不完就继续挂起——这意味着:
- 一个 worker 能同时跟踪数万连接的写状态,而无需为每个连接分配线程或协程;
- 高延迟链路(如弱网手机)的连接不会拖慢其他连接的响应速度;
- 即使某个连接因网络卡顿长期处于“写挂起”状态,也不影响新请求接入和其它连接的读/写推进。
sendfile 零拷贝大幅减少用户态写压力
对于静态文件响应,Nginx 默认启用 sendfile on;,绕过用户态内存拷贝:
- 内核直接从磁盘读取文件内容,写入 socket 发送缓冲区;
- 整个过程不经过 Nginx 用户空间,不触发 write() 系统调用,也就无需 Event Loop 主动管理写事件;
- 仅在 sendfile 完成后触发一次“写完成”通知(或由 TCP ACK 触发后续逻辑),极大降低写路径开销。
反向代理场景下,写操作与上游读操作解耦
当 Nginx 作为代理转发请求时,“向客户端写响应”和“从 upstream 读响应”是两个独立事件流:
- 上游返回部分数据 → 缓存到 output buffer → 注册 EPOLLOUT → 等待可写;
- 上游还在传输 → 当前连接保持 write event 注册状态,Event Loop 继续处理其他事件;
- 同一 worker 可同时维护前端连接的写挂起、后端连接的读就绪、定时器超时等多类状态,全部靠事件驱动协同推进。
本质上,Nginx 的 Event Loop 不是“执行写”,而是“调度写时机”。它把操作系统内核的能力(epoll 可写通知、sendfile 零拷贝、TCP 缓冲区管理)组织成一套轻量、无锁、可伸缩的状态机,这才是它扛住高并发写流量的根本原因。











