Nginx采用异步事件驱动模型处理非阻塞套接字,核心是“不等待、不分配、不切换”:所有socket设为non-blocking,通过epoll监听事件,事件循环按需分发,连接状态与事件绑定,单worker可高效处理万级并发。

Nginx 的异步事件驱动模型处理非阻塞套接字,核心在于“不等待、不分配、不切换”——它不为每个连接开线程,不因某次读写卡住整个进程,也不依赖内核轮询,而是靠操作系统事件通知 + 用户态高效调度协同完成。
非阻塞套接字是前提,不是可选项
所有 socket(监听套接字和已建立连接的套接字)在创建后立即设为 non-blocking 模式。这意味着:
- accept() 调用不会挂起:只要监听 socket 就绪,就循环调用 accept() 直到返回 EAGAIN,把排队的连接全收进来;
- recv() 无数据时立刻返回 EAGAIN/EWOULDBLOCK,而不是等;
- send() 只发当前发送缓冲区允许的字节数,剩余数据留在连接的 output buffer 中,同时注册写事件等待下次可写;
- 任何阻塞操作都会破坏事件循环节奏,所以 Nginx 在初始化阶段就强制设置 SOCK_NONBLOCK 或调用 fcntl 设置 O_NONBLOCK。
epoll 是事件感知的中枢
Linux 下,每个 worker 进程启动时创建一个 epoll 实例,把所有待监控的 socket(包括监听 socket 和已连接 socket)加入 epoll 红黑树,并注册关心的事件类型(EPOLLIN/EPOLLOUT/EPOLLERR):
- 监听 socket 关注 EPOLLIN:有新连接到达;
- 已连接 socket 初始只关注 EPOLLIN:等待客户端发来请求头;
- 当需要发送响应但内核发送缓冲区满时,临时注册 EPOLLOUT;
- 一旦 socket 出错或对端关闭,epoll_wait 会返回 EPOLLERR 或 EPOLLHUP,触发错误清理逻辑。
事件循环按需分发,不空转也不死等
ngx_process_events_and_timers 是主干函数,每次执行都做三件事:
- 先尝试抢 accept 锁(避免惊群),有权限则批量 accept 新连接;
- 调用 epoll_wait,超时时间取自定时器红黑树最小值(比如最近一个 keepalive timeout);
- epoll_wait 返回后,遍历就绪事件链表(rdllist),对每个就绪 socket 调用绑定的 handler(如 ngx_http_wait_request_handler);
- 若无就绪事件但定时器到期,则触发超时处理(如关闭空闲连接、重试 upstream、刷新健康检查)。
连接状态与事件始终绑定
每个活跃连接对应一个 ngx_connection_t 结构体,里面直接持有:
- socket fd 和非阻塞标志;
- 读/写事件对象(ngx_event_t),含回调函数指针;
- input/output buffer 地址与长度;
- read/write timeout 定时器节点,插入全局红黑树统一管理;
- 与 epoll 的关联通过 event->index 记录在 epoll_data_t 中,便于事件到来时快速定位连接上下文。
整个过程没有线程创建、没有阻塞等待、没有轮询浪费 CPU。一个 worker 进程就能靠这套机制持续吞吐成千上万连接,关键就在于非阻塞套接字提供了“随时可中断”的基础,而事件驱动让中断后的控制流能精准回到正确位置继续推进。



















