nginx 的 non-blocking i/o 核心是让单 worker 同时处理成千上万连接,而非提速单次读写;它通过 o_nonblock + epoll/kqueue 实现事件驱动,配合动态缓冲、零拷贝 sendfile 和写事件队列,避免阻塞与资源浪费。

Nginx 的 Non-blocking I/O 不是“加快单次读写速度”的技巧,而是让一个 worker 进程能同时盯住成千上万个连接,不卡在等数据的路上——这才是它扛住高并发的底层逻辑。
非阻塞 I/O 怎么避免“干等”
传统阻塞模式下,调用 recv() 时若没数据,进程就挂起,啥也不干;而 Nginx 在 socket 上设了 O_NONBLOCK 标志,每次调用立刻返回:
- 有数据就读走,继续处理请求头或请求体
- 没数据就收到 EAGAIN/EWOULDBLOCK,不等待,直接转向下一个就绪连接
- 靠 epoll/kqueue 告诉它“哪个 socket 现在可读”,它才去 recv,绝不盲试
事件驱动 + 缓冲区策略协同发力
非阻塞只是前提,真正提升效率的是它和缓冲机制的配合:
- 读缓冲按需分配:HTTP 头未收全时动态扩容,但不超过 large_client_header_buffers;超大上传自动落盘到 client_body_temp_path,防内存爆掉
- 写响应走零拷贝路径:开启 sendfile on 后,静态文件由内核直接从磁盘送入 socket,跳过用户态内存拷贝
- 发送遇阻(如网卡慢)时,把连接挂进 write event 队列,下次 epoll 返回“可写”再续发,不占着 worker 干耗
配置上释放 Non-blocking 的潜力
Non-blocking 默认启用,但要让它跑得稳、跑得满,得调好几处关键参数:
- worker_processes auto:让每个 CPU 核跑一个 worker,避免跨核调度损耗
- worker_connections 10240:值要 ≥ 系统 ulimit -n 限制,否则新连接连不上
- client_header_timeout 15s 和 client_body_timeout 60s:及时清理发一半就断连的客户端,腾出事件槽位
- 避免在 Lua 或自定义模块里做同步 DNS 查询、sleep 或长循环——这些会堵死整个 worker,必须改用 cosocket 或异步接口
为什么它比“一连接一线程”省资源
不是因为代码写得更聪明,而是彻底绕开了操作系统调度的开销:
- 不用为每个连接分配栈空间(线程默认 1–8MB),一个连接只占几 KB 的 ngx_connection_t 结构体
- 没有线程切换成本:CPU 不在上下文切换中浪费周期,全花在处理真实数据上
- 慢客户端上传不影响快客户端响应:epoll 只通知就绪事件,worker 哪个先到就先服务哪个











