nginx 源码分析:http 请求处理流程——请求处理的 11 个阶段

nginx 将处理 http 的请求分为 11 个阶段,每个阶段的作用不同,内置的一些模块都在不同的阶段实现它们的功能。这些阶段定义为如下的枚举类型:

typedef enum {
    NGX_HTTP_POST_READ_PHASE = 0,

    NGX_HTTP_SERVER_REWRITE_PHASE,

    NGX_HTTP_FIND_CONFIG_PHASE,
    NGX_HTTP_REWRITE_PHASE,
    NGX_HTTP_POST_REWRITE_PHASE,

    NGX_HTTP_PREACCESS_PHASE,

    NGX_HTTP_ACCESS_PHASE,
    NGX_HTTP_POST_ACCESS_PHASE,

    NGX_HTTP_PRECONTENT_PHASE,

    NGX_HTTP_CONTENT_PHASE,

    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

第一个阶段是 POST_READ 阶段。ngx_http_realip_module 模块就工作在这个阶段,它可以根据 X-Forwarded-For 等头部,提取出客户端的真实 IP。

第二个阶段是 SERVER_REWRITE 阶段。ngx_http_rewrite_module 模块工作在这个阶段,用于实现 server{} 配置里的 url 重写功能。

第三个阶段是 FIND_CONFIG 阶段。没有模块能够介入这个阶段。这个阶段的功能是根据请求的 url 查找到匹配的 locaiton{} 。

第四个阶段是 REWRITE 阶段。ngx_http_rewrite_module 也工作在这个阶段,用于处理 location{} 配置里的 url 重写。

第五个阶段是 POST_REWRITE 阶段。没有模块能够介入这个阶段。这个阶段完全是为了配合 ngx_http_rewrite_module 模块。

第六个阶段是 PREACCESS 阶段。ngx_http_limit_conn_modulengx_http_limit_req_module 模块工作在这个阶段,它们分别实现并发连接数限制和请求速率限制功能。

第七个阶段是 ACCESS 阶段。ngx_http_access_modulengx_http_auth_basic_module 工作在这个阶段,它们分别实现访问控制和基本认证的功能。

第八个阶段是 POST_ACCESS 阶段。没有模块能够介入这个阶段。这个阶段是为了配合 ACCESS 阶段。

第九个阶段是 PRECONTENT 阶段。ngx_http_mirror_module 工作在这个阶段,实现流量镜像的功能。

第十个阶段是 CONTENT 阶段。这是最关键的一个阶段,也是外部模块最喜欢介入的阶段。ngx_http_index_modulengx_http_static_module 工作在这个阶段实现返回静态页面的功能。后面分析代码,我们也是重点关注这个阶段。

最后一个是 LOG 阶段。ngx_http_log_module 模块工作在这个阶段,实现输出访问日志的功能。


那么这些模块是怎样介入这些阶段呢?我们先看看 ngx_http_index_module 模块在解析完配置后会被调用的函数 ngx_http_index_init

static ngx_int_t
ngx_http_index_init(ngx_conf_t *cf)
{
    ngx_http_handler_pt        *h;
    ngx_http_core_main_conf_t  *cmcf;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, " ");

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_index_handler;

    return NGX_OK;
}

它往 cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers 数组里添加了一个 handler ,这个 handler 是 ngx_http_index_handler。这样在执行到这个阶段就会调用这个 handler 处理。

typedef struct {
	...
    ngx_http_phase_engine_t    phase_engine;
    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
    ...
} ngx_http_core_main_conf_t;

可以看到,core 模块创建的 main 级别的配置结构 ngx_http_core_main_conf_t 里有一个 phases 数组,它的大小是 11,每个阶段都用 ngx_http_phase_t 结构体表示。

typedef struct {
    ngx_array_t                handlers;
} ngx_http_phase_t;

我们看到这结构实际上就是一个数组,每一项都是一个函数指针,指向一个 handler 。它的原型是:

typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);

我们再看看另一个成员 phase_engine,它的结构是 ngx_http_phase_engine_t

typedef struct {
    ngx_http_phase_handler_t  *handlers;
    ngx_uint_t                 server_rewrite_index;
    ngx_uint_t                 location_rewrite_index;
} ngx_http_phase_engine_t;

它有一个 handlers 成员,指向一个 ngx_http_phase_handler_t 结构体数组,这个结构的定义如下:

struct ngx_http_phase_handler_s {
    ngx_http_phase_handler_pt  checker;
    ngx_http_handler_pt        handler;
    ngx_uint_t                 next;
};

在这里我们发现了 handler 。

那么 phase_engine 成员是怎么初始化的呢?就来看看初始化它的函数 ngx_http_init_phase_handlers

ngx_http_init_phase_handlers

它被 ngx_http_block 调用,这时候各个模块已经添加了 handler。

    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
    find_config_index = 0;
    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;

    n = 1                  /* find config phase */
        + use_rewrite      /* post rewrite phase */
        + use_access;      /* post access phase */

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        n += cmcf->phases[i].handlers.nelts;
    }

    ph = ngx_pcalloc(cf->pool,
                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
    if (ph == NULL) {
        return NGX_ERROR;
    }
 
    cmcf->phase_engine.handlers = ph;

先计算出所有的 handler 数量,分配相应的内存。

    n = 0;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        h = cmcf->phases[i].handlers.elts;

        switch (i) {

        case NGX_HTTP_SERVER_REWRITE_PHASE:
            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.server_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_FIND_CONFIG_PHASE:
            find_config_index = n;

            ph->checker = ngx_http_core_find_config_phase;
            n++;
            ph++;

            continue;

        case NGX_HTTP_REWRITE_PHASE:
            if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.location_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_POST_REWRITE_PHASE:
            if (use_rewrite) {
                ph->checker = ngx_http_core_post_rewrite_phase;
                ph->next = find_config_index;
                n++;
                ph++;
            }

            continue;

        case NGX_HTTP_ACCESS_PHASE:
            checker = ngx_http_core_access_phase;
            n++;
            break;

        case NGX_HTTP_POST_ACCESS_PHASE:
            if (use_access) {
                ph->checker = ngx_http_core_post_access_phase;
                ph->next = n;
                ph++;
            }

            continue;

        case NGX_HTTP_CONTENT_PHASE:
            checker = ngx_http_core_content_phase;
            break;

        default:
            checker = ngx_http_core_generic_phase;
        }

        n += cmcf->phases[i].handlers.nelts;

        for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {
            ph->checker = checker;
            ph->handler = h[j];
            ph->next = n;
            ph++;
        }
    }

    return NGX_OK;

然后根据每个阶段,设置 ngx_http_phase_handler_s 结构中的 checker、handler、next。

handler 指向各个模块添加的 handler 。

注意,里面的 for 循环是从后向前遍历的,所以后添加的 handler 先执行。

checker 指向 ngx_http_core_rewrite_phasengx_http_core_find_config_phasengx_http_core_post_rewrite_phasengx_http_core_access_phasengx_http_core_post_access_phasengx_http_core_content_phasengx_http_core_generic_phase 之一。因为每个阶段的作用不一样,所以需要指定不同的 checker,来根据 handler 的返回结果执行不同的流程。

每个 handler 在数组里的索引即是它的编号。

next 成员是下一个阶段的第一个 handler 的编号。这样,可以直接跳到下一个阶段执行。

注意,switch 语句里在处理 FIND_CONFIGPOST_REWRITEPOST_ACCESS 阶段时,只设置了 checker,然后就 continue 了。所以这三个阶段只有 chcker,而没有 handler,模块是不能往这几个阶段添加 handler 的。

在循环的过程中,phase_engine成员中的 server_rewrite_index 的值被设置成 SERVER_REWRITE 阶段中重写 server{} 里 url 的 handler 的编号。server_rewrite_index 的值被设置成 REWRITE 阶段中重写 location{} 里 url 的 handler 的编号。这样,就能直接跳到这两个阶段执行了。


了解完初始化后,再看下处理流程的代码。

ngx_http_process_request

读取完请求后(不含包体),就进入请求处理阶段。由 ngx_http_process_request 函数开始。

void
ngx_http_process_request(ngx_http_request_t *r)
{
    ngx_connection_t  *c;

    c = r->connection;

    if (c->read->timer_set) {
        ngx_del_timer(c->read);
    }

    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;
    r->read_event_handler = ngx_http_block_reading;

    ngx_http_handler(r);

    ngx_http_run_posted_requests(c);
}

将连接的读写事件 handler 设置为 ngx_http_request_handler

将请求的读事件 handler 设置为 ngx_http_block_reading

然后调用 ngx_http_handler 函数处理请求。

最后调用 ngx_http_run_posted_requests 函数执行子请求。子请求的内容我会单独发一篇文章,这里就不提了。

ngx_http_request_handler

先看 ngx_http_request_handler,它的代码很简单:

static void
ngx_http_request_handler(ngx_event_t *ev)
{
    ngx_connection_t    *c;
    ngx_http_request_t  *r;

    c = ev->data;
    r = c->data;

    ngx_http_set_log_request(c->log, r);

    if (ev->delayed && ev->timedout) {
        ev->delayed = 0;
        ev->timedout = 0;
    }

    if (ev->write) {
        r->write_event_handler(r);

    } else {
        r->read_event_handler(r);
    }

    ngx_http_run_posted_requests(c);
}

之后,当连接上出现读事件时,就调用请求的读事件 handler 。

当出现写事件时,就调用请求的写事件 handler 。

最后再执行子请求。

ngx_http_handler

void
ngx_http_handler(ngx_http_request_t *r)
{
    ngx_http_core_main_conf_t  *cmcf;

    r->connection->log->action = NULL;

    if (!r->internal) {
        switch (r->headers_in.connection_type) {
        case 0:
            r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
            break;

        case NGX_HTTP_CONNECTION_CLOSE:
            r->keepalive = 0;
            break;

        case NGX_HTTP_CONNECTION_KEEP_ALIVE:
            r->keepalive = 1;
            break;
        }

        r->lingering_close = (r->headers_in.content_length_n > 0
                              || r->headers_in.chunked);
        r->phase_handler = 0;

    } else {
        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
        r->phase_handler = cmcf->phase_engine.server_rewrite_index;
    }

    r->valid_location = 1;
    r->write_event_handler = ngx_http_core_run_phases;
    ngx_http_core_run_phases(r);
}

如果请求是外部的请求,则根据情况设置是否打开 Keepalive 功能。后面我单独发一篇文章分析 Keepalive 的实现。

然后将 phase_handler 设置为0,表示需要从第一个阶段 POST_READ 开始处理请求。

如果是内部的请求,则将 r->phase_handler 设置为 server_rewrite_index,表示需要从 SERVER_REWRITE 阶段开始处理请求。

然后将请求的写事件 handler 设置为 ngx_http_core_run_phases,并调用它。这样,当请求不能一次处理完成,之后连接上出现写事件时,就调用它继续处理。

ngx_http_core_run_phases

这个函数调用每个阶段的 checker 执行它们的 handler。

void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
    ngx_int_t                   rc;
    ngx_http_phase_handler_t   *ph;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    ph = cmcf->phase_engine.handlers;

    while (ph[r->phase_handler].checker) {

        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        if (rc == NGX_OK) {
            return;
        }
    }
}

循环从编号为 r->phase_handler 的 handler 所在的阶段中开始执行。当它的 checker 返回 NGX_OK 时,退出循环,整个事件处理结束。否则,可能执行当前阶段的下一个 handler 或者跳到其他阶段执行。

下篇文章分析 CONTENT 阶段的 checker 函数 ngx_http_core_content_phase,和 ngx_http_index_modulengx_http_static_module这两个模块的 handler 代码。

nginx 源码分析:http 请求处理流程——请求处理的 11 个阶段nginx 源码分析:http 请求处理流程——请求处理的 11 个阶段 alenliu0621 发布了111 篇原创文章 · 获赞 45 · 访问量 30万+ 私信 关注
上一篇:一、VIP课程:互联网工程专题 04-Maven私服使用与插件开发


下一篇:Kubernetes---Pod phase