事件驱动与异步I/O
Nginx 基于事件驱动模型实现高并发,通过 I/O 多路复用技术,单进程即可处理数万连接。
事件模块架构
核心结构体
C
typedef struct {
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*enable)(ngx_connection_t *c, ngx_uint_t event);
ngx_int_t (*disable)(ngx_connection_t *c, ngx_uint_t event);
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
事件模块初始化
C
static ngx_int_t
ngx_event_process_init(ngx_cycle_t *cycle)
{
// 选择事件模块(epoll/kqueue/select)
module = ngx_event_core_module.ctx;
// 创建连接数组
cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_num, cycle->log);
cycle->files = ngx_calloc(sizeof(ngx_connection_t*) * cycle->files_n, cycle->log);
// 连接与空闲链表绑定
for (i = 0; i < cycle->connection_num; i++) {
c = &cycle->connections[i];
c->data = NULL;
c->read = &cycle->read_events[i];
c->write = &cycle->write_events[i];
c->read->data = c;
c->write->data = c;
ngx_queue_insert_head(&ngx_posted_events, &c->queue);
}
return NGX_OK;
}
I/O 多路复用
epoll 模块
C
static ngx_int_t
ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
{
struct epoll_event ee;
ee.events = EPOLLIN|EPOLLOUT|EPOLLET; // 边沿触发
ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee);
}
static ngx_int_t
ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
{
events = epoll_wait(ep, event_list, (int) nevents, timer);
for (i = 0; i < events; i++) {
c = event_list[i].data.ptr;
instance = (uintptr_t) c & 1;
c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);
rev = c->read;
if (c->fd == -1 || rev->instance != instance) {
continue; // 过期事件
}
if (event_list[i].events & EPOLLIN) {
rev->ready = 1;
rev->handler(rev); // 调用读事件处理器
}
}
}
epoll 使用边沿触发(ET)模式,需一次性读写所有可用数据,否则事件不再触发。
事件模块选择策略
| 模块 | 平台 | 特点 |
|---|---|---|
| epoll | Linux 2.6+ | 高性能,支持 ET/LT |
| kqueue | FreeBSD/macOS | 高效,支持异步 I/O |
| /dev/poll | Solaris | 高效 |
| select | 跨平台 | 1024 FD 限制,轮询 |
| poll | 跨平台 | 无 FD 限制,轮询 |
定时器机制
事件定时器
C
// 添加定时器
ngx_add_timer(ev, 30000); // 30 秒超时
// 删除定时器
ngx_del_timer(ev);
// 定时器管理
ngx_rbtree_t ngx_event_timer_rbtree;
static ngx_inline void
ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
ngx_msec_t key;
ngx_msec_int_t diff;
key = ngx_current_msec + timer;
if (ev->timer_set) {
ngx_del_timer(ev);
}
ev->timer.key = key;
ev->timer_set = 1;
ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);
}
异步请求处理
非阻塞 I/O 处理链
C
static void
ngx_http_my_read_handler(ngx_event_t *rev)
{
ngx_connection_t *c = rev->data;
ngx_http_my_ctx_t *ctx = c->data;
// 读取数据
n = c->recv(c, ctx->buf, ctx->size);
if (n == NGX_AGAIN) {
// 数据未就绪,等待下次事件
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_finalize_request(ctx->r, NGX_HTTP_INTERNAL_SERVER_ERROR);
}
return;
}
if (n <= 0) {
ngx_http_finalize_request(ctx->r, NGX_HTTP_BAD_GATEWAY);
return;
}
// 数据读取完成,继续处理
ngx_http_my_process_data(ctx);
}
static ngx_int_t
ngx_http_my_send_handler(ngx_http_request_t *r)
{
// 设置写事件处理器
c->write->handler = ngx_http_my_write_handler;
// 触发写事件
ngx_http_my_write_handler(c->write);
return NGX_OK;
}
事件驱动状态机
text
连接建立
→ 注册读事件 (epoll add)
→ epoll_wait 等待事件
→ 读事件触发
→ 读取数据 (recv)
→ 数据不完整:ngx_handle_read_event 重新注册,返回
→ 数据完整:处理请求
→ 生成响应
→ 注册写事件
→ 写事件触发
→ 发送数据 (send)
→ 发送完成:关闭连接或 keepalive
要点总结
- Nginx 通过 epoll/kqueue 等 I/O 多路复用实现单进程高并发
- 事件模块通过
ngx_event_actions_t抽象,支持多种 I/O 复用技术 - epoll 使用边沿触发模式,需一次性读写所有可用数据
- 定时器基于红黑树管理,支持高效超时处理
- 异步处理通过事件状态机实现,读/写事件分离,避免阻塞
📝 发现内容有误?点击此处直接编辑