非阻塞io高效管理海量并发的核心在于与事件驱动、内核机制(epoll/kqueue)、资源约束深度协同:socket设为非阻塞,事件循环按就绪状态调度读写,连接拆解为状态机,并辅以超时与连接数限制防资源耗尽。

非阻塞 IO 本身不是魔法,它高效管理成千上万个并发套接字的关键,在于和事件驱动、内核机制、资源约束三者深度咬合——单个 worker 进程不靠堆线程,而是靠“只响应就绪”来腾出 CPU 去轮询数万连接。
socket 全程设为非阻塞是前提
每个新 accept 的连接,Nginx 都会调用 fcntl() 设置 O_NONBLOCK 标志。这意味着:
- recv() 没数据时立刻返回 EAGAIN/EWOULDBLOCK,绝不挂起进程
- send() 发不完(比如 TCP 窗口满)也立即返回 EAGAIN,不等缓冲区腾空
- 后续读写动作,全由事件循环根据就绪状态决定何时继续,而非靠超时或轮询
epoll/kqueue 批量监听就绪事件
Linux 下默认启用 epoll,FreeBSD/macOS 用 kqueue。它们让 Nginx 能一次性监控成千上万个 socket 的状态变化:
- 注册所有监听 socket 和已建立连接的 fd 到 epoll 实例中
- 调用 epoll_wait() 一次就能拿到多个就绪事件(例如 50 个连接同时可读)
- 避免 select/poll 的 O(n) 扫描开销,epoll 是 O(1) 通知,内核直接告诉“谁有事”
连接生命周期被拆解为可挂起的状态机
一个 HTTP 请求不再是一条直通到底的同步流程,而是按事件分阶段推进:
- TCP 握手完成 → 触发 accept 事件,接收连接并注册 EPOLLIN
- 客户端慢发请求头 → 每次 read() 返回 EAGAIN 后,继续监听该 fd 的 EPOLLIN,不释放也不阻塞
- 转发请求给 upstream → 当前连接挂起,worker 立刻转去处理其他就绪连接
- 后端响应到达 → 触发读事件;尝试 send() 响应 → 若又遇 EAGAIN,则注册 EPOLLOUT,等可写再续发
必须配合超时与连接限制防资源耗尽
非阻塞不能代替约束,否则慢连接会堆积 fd、占用内存、拖慢整体响应:
- client_header_timeout 5s、client_body_timeout 10s:防 Slowloris 类攻击,头部或体发送太慢即断连
- send_timeout 15s:两次 write 之间最大间隔,避免弱网客户端长期占连接
- keepalive_timeout 15–60s:空闲长连接及时释放,回收 fd 资源
- worker_connections 65536 需匹配系统 ulimit -n,确保 epoll 能管够多 socket











