non-blocking io、事件循环与回调函数三者协同构成异步非阻塞架构:non-blocking使i/o调用不阻塞线程,事件循环(如epoll_wait)检测fd就绪状态并分发,回调函数在就绪时执行业务逻辑且不可阻塞,三者咬合实现单worker承载数万连接。

Non-blocking IO 是异步事件驱动架构的底层前提,不是配套选项,而是启动整个机制的必要条件。没有非阻塞,事件循环就无事可分发;没有事件循环调度,非阻塞调用就只是反复轮询的低效空转。
Non-blocking 是事件驱动的物理基础
每个 socket 在 accept 后立即被设为 non-blocking 模式(如通过 fcntl(fd, F_SETFL, O_NONBLOCK))。这意味着:
- read() 在无数据时立刻返回 EAGAIN/EWOULDBLOCK,不挂起 worker 线程
- write() 在缓冲区满时也立即返回错误,而非等待内核腾出空间
- 所有 I/O 调用都变成“试探性”操作:成功则推进逻辑,失败则交由事件循环后续接管
事件循环是状态感知与分发中枢
epoll_wait 或 kqueue 不执行业务,只做一件事:定期检查哪些 fd 已就绪(可读、可写、出错),并把结果列表交给 Nginx 主循环。
- 它不关心请求头是否收完、响应体是否发完,只反馈“这个 fd 现在可以安全 read/write 了”
- 就绪事件本身不携带上下文,真正承载业务逻辑的是预先注册的回调函数
- 一次 epoll_wait 返回多个就绪 fd,Nginx 可批量触发对应回调,避免频繁系统调用开销
回调函数是业务逻辑的执行单元
模块在发起 I/O 前必须注册回调(例如 ngx_http_read_request_handler),而回调的执行时机完全由事件循环决定。
- 回调只处理当前就绪状态:比如收到部分请求头,就解析;若不完整,就继续注册读事件等待下次就绪
- 不能在回调里做阻塞操作(如同步 MySQL 查询、大文件 read),否则会卡住整个 worker
- 每次回调结束前,需明确决定下一步动作——重注册事件、设置定时器、关闭连接或转入下一阶段
三者垂直贯通才构成真正的异步非阻塞
单独开启 non-blocking 或使用 epoll 并不等于高并发。Nginx 的能力来自三者的咬合:
- socket 层 non-blocking 让 worker 不被单个连接拖住
- 事件循环让 worker 始终有事可做,而不是空等或忙轮询
- 回调机制把复杂请求拆成可中断、可恢复、可取消的小步骤,形成轻量状态机
从 accept 到 close,每个环节都遵循这套协作逻辑,单 worker 才能稳定承载数万连接。











