Nginx高性能核心在于事件驱动的非阻塞I/O模型,体现为事件循环(ngx_process_events_and_timers)、模块注册(如ngx_epoll_module)、回调调度(handler函数指针)与内存复用(request pool、ngx_connection_t复用)四者协同。

nginx 的高性能核心在于其基于事件驱动的非阻塞 I/O 模型,这一模型并非抽象概念,而是直接贯穿于源码结构、模块协作与执行流程之中。理解它在源码中的映射,关键不是逐行读 epoll 或 kqueue,而是看清“事件循环—模块注册—回调调度—内存复用”这条主线如何具象为 ngx_event.c、ngx_epoll_module.c、ngx_http_request.c 等文件间的调用关系与数据流转。
事件循环:从 ngx_process_events_and_timers 到具体 IO 多路复用器
nginx 启动 worker 进程后,核心循环落在 ngx_process_cycle.c 中的 ngx_worker_process_cycle 函数,它不断调用 ngx_process_events_and_timers —— 这是事件分发的统一入口。该函数不直接处理 socket,而是委托给当前启用的事件模块(如 epoll、kqueue、select)实现的 process_events 钩子。
- 在
ngx_epoll_module.c中,ngx_epoll_process_events调用epoll_wait获取就绪事件,将 fd 和事件类型(EPOLLIN/EPOLLOUT)封装为ngx_event_t *,并触发其handler回调; - 所有网络事件(accept、read、write)最终都绑定到某个
ngx_event_t实例上,而该实例的data字段通常指向连接结构ngx_connection_t; - 真正决定“做什么”的不是事件本身,而是 handler 函数指针:比如新连接由
ngx_event_accept处理,HTTP 请求读取由ngx_http_init_request触发的后续读 handler 承担。
连接与请求生命周期:非阻塞如何体现在内存与状态机中
非阻塞不只是系统调用返回快,更是整个请求处理被拆解为多个可中断、可重入的状态片段,并复用固定内存块(如 ngx_pool_t 分配的 request pool)避免频繁分配释放。
-
ngx_connection_t是连接层核心,包含读写事件read/write(均为ngx_event_t *),其handler在 accept 后设为ngx_http_init_request; - HTTP 请求解析不一次性读完全部 body,而是按需触发:header 读完触发
ngx_http_process_request_headers,body 未就绪时将读事件再次添加进事件队列,等待下次 epoll_wait 唤醒; - 每个请求对应一个
ngx_http_request_t,但它不长期驻留内存——一旦响应发送完毕或超时关闭,整个 request pool 被整体销毁,连接结构则可能被回收进空闲链表供复用(keepalive 场景下)。
模块协同:handler 链与 phase handler 如何适配事件驱动
HTTP 模块的处理逻辑(如 rewrite、access、content)并非顺序同步执行,而是注册为不同 phase 的 handler,在事件就绪时由核心框架按阶段调用,每个 handler 必须支持“中途挂起”和“恢复执行”。
- 例如
ngx_http_core_content_phase在收到完整请求后启动 phase handler 链,若某 handler(如 upstream 模块发起反向代理请求)需要等待后端响应,则主动将当前请求的写事件设为“等待”,自身返回NGX_AGAIN,控制权交还事件循环; - 后端响应到达时,upstream 模块的 read handler 被唤醒,继续执行后续 content phase 或直接输出;
- 这种“注册—挂起—唤醒”机制依赖于 request 结构体中
write_event_handler和read_event_handler的动态切换,而非线程栈保存上下文。
零拷贝与缓冲区管理:非阻塞 I/O 的性能落点
非阻塞模型若配合低效内存操作,性能仍会受限。nginx 通过分层缓冲区设计与系统调用优化,让 I/O 尽可能贴近内核零拷贝能力。
-
ngx_buf_t描述一段内存或文件区域,配合sendfile(Linux)或sendfile64(FreeBSD)可实现内核态直接传输,绕过用户态拷贝; - 读缓冲区(
recv_buffer)默认 16KB,但实际接收使用recv+MSG_PEEK探测包头,再按需截取,避免一次读太多导致内存浪费或解析错位; - 写操作采用“链式 buffer”(
ngx_chain_t),允许 header、body、footer 分散在不同内存块,由ngx_output_chain统一调度,必要时合并或分片,适配 TCP MSS 与 sendfile 边界。
不复杂但容易忽略:nginx 的非阻塞本质不是“不用 wait”,而是“用一次 wait 管理成千上万连接的状态变迁”,所有源码结构都服务于这个目标——事件是消息,handler 是行为,内存是资源,而循环是心跳。

















