全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-20 9 分钟 ✍️ juanwangdev

事件驱动与异步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)模式,需一次性读写所有可用数据,否则事件不再触发。

事件模块选择策略

模块平台特点
epollLinux 2.6+高性能,支持 ET/LT
kqueueFreeBSD/macOS高效,支持异步 I/O
/dev/pollSolaris高效
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 使用边沿触发模式,需一次性读写所有可用数据
  • 定时器基于红黑树管理,支持高效超时处理
  • 异步处理通过事件状态机实现,读/写事件分离,避免阻塞

📝 发现内容有误?点击此处直接编辑

← 上一篇 Nginx模块架构与生命周期
下一篇 → 内存池与数据结构
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库