Nginx源码剖析--HTTP请求的分阶段处理的初始化


前言

Nginx作为一个http服务器,核心任务就是处理HTTP请求。在接收到请求时,Nginx框架首先解析http请求,将解析结果放在ngx_http_request中,由于http是在tcp上工作的,因此解析可能会持续一段时间。nginx用状态机完成对HTTP请求的异步解析。整个解析过程都是由Nginx框架代码完成,不需要用户介入。当解析得到完整的http请求后,就开始处理http请求。nginx对http请求的处理是分阶段进行的。对请求的处理也是用户主要介入nginx的地方。对于一个http请求,nginx主要分以下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_TRY_FILES_PHASE,
    NGX_HTTP_CONTENT_PHASE,

    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

这篇博文将简单介绍一下这些处理请求时,这些阶段是怎么介入进去的。


http请求的处理 – ngx_http_handler

http请求处理的入口是ngx_http_process_request函数:

void
ngx_http_process_request(ngx_http_request_t *r)

在ngx_http_process_request函数中,我们可以看到:

    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_request_handler处理函数。

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

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

    ctx = c->log->data;
    ctx->current_request = r;

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "http run request: \"%V?%V\"", &r->uri, &r->args);

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

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

    ngx_http_run_posted_requests(c);
}

我们可以看到这个函数主要是通过调用r->read_event_handler和r->write_event_handler来完成任务。

而从ngx_http_process_request里面可以看到:

r->read_event_handler = ngx_http_block_reading;

r->write_event_handler在ngx_http_handler函数中被设置:

r->write_event_handler = ngx_http_core_run_phases;

由于处理http请求主要是处理写事件,因为接到http请求之后,后面需要做的就是将响应写到客户端。

因此我们可以看到,nginx处理http请求主要是在ngx_http_core_run_phases函数中完成:

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); //获得ngx_http_core_module模块的main_conf配置结构体

    ph = cmcf->phase_engine.handlers; 

    while (ph[r->phase_handler].checker) { //逐一执行各个阶段的checker函数

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

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

可以看到,Nginx依次调用各个阶段的checker函数处理请求。请求的当前处理阶段是通过

r->phase_handler

来标识。

各个阶段处理函数的初始化

从前面可以知道,各个阶段的处理函数都在ngx_http_core_module模块的main_conf的phase_engine.handlers成员中。phase_engine.handlers是一个数组,数组类型是

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

也就是说,每个元素包含三个成员,分别是checker函数,handler函数,和next。checker函数是对外的接口,handler一般是由checker函数调用,next表示下一个阶段被调用的首个处理函数在数组中的位置。因此对各个阶段处理函数的初始化就是对phase_engine.handlers是一个数组的初始化。这个初始化和两个函数相关:

static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf);

static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)

这两个函数都是在ngx_http_block函数中被调用的。

ngx_http_init_phases

static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
                       cf->pool, 2, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
                       cf->pool, 4, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    return NGX_OK;
}

这个函数很简单,主要是对几个数组进行初始化。

ngx_http_core_module的main_conf配置结构体中的phases是一个数组

ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];

数组中的每个元素的类型是

typedef struct {
    ngx_array_t                handlers;
} ngx_http_phase_t;

所以phases是一个二维数组。phase数组中的每个元素表示对应一个阶段的handler函数数组。这是为了后面初始化phase_engine.handlers做准备的。

Nginx模块编写用户可以通过向具体的阶段添加handler函数来实现http请求的处理干涉。一般的方法就是向phases中对应的某个阶段的handler数组中添加。如要向第( i )阶段添加一个handler,则将handler函数 push 到phases[i]数组中就可以了,这个介入一般是通过调用http模块的postconfiguration成员函数实现。

我们从这个函数可以看到,函数中只初始化了7个阶段对应的数组,而不是所有的11个阶段的数组都被初始化。因此,可以发现,Nginx当前只支持用户介入处理http请求的七个阶段:

1. NGX_HTTP_POST_READ_PHASE
2. NGX_HTTP_SERVER_REWRITE_PHASE
3. NGX_HTTP_REWRITE_PHASE
4. NGX_HTTP_PREACCESS_PHASE
5. NGX_HTTP_ACCESS_PHASE
6. NGX_HTTP_CONTENT_PHASE
7. NGX_HTTP_LOG_PHASE

ngx_http_init_phase_handlers

这个函数中主要是用上一个函数,以及各个http模块通过调用自己的postconfiguration函数初始化后的cmcf->phase二维数组对cmcf->phase_engine.handlers数组进行初始化,cmcf->phase_engine.handlers是处理http请求是实际使用的东西。这里需要注意的是,cmcf->phase_engine.handlers是一个一维数组。下面我们具体看一下这个函数的实现:

//为cmcf->phase_engine.handlers分配空间
static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    ngx_int_t                   j;
    ngx_uint_t                  i, n;
    ngx_uint_t                  find_config_index, use_rewrite, use_access;
    ngx_http_handler_pt        *h;
    ngx_http_phase_handler_t   *ph;
    ngx_http_phase_handler_pt   checker;

    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 = use_rewrite + use_access + cmcf->try_files + 1 /* find config 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;

这部分代码的主要功能是为cmcf->phase_engine.handlers分配空间。其他功能我们在后面详细介绍各个阶段的功能时在说。

//用cmcf->phase二维数组对cmcf->phase_engine.handlers数组进行初始化
    n = 0;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        //h指向当前阶段的handler数组
        h = cmcf->phases[i].handlers.elts;

        switch (i) {
        // 凡是有continue语句的,表示这个阶段的处理方法只能由HTTP框架实现
        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_TRY_FILES_PHASE: // 不允许加入模块自定义的处理方法
            if (cmcf->try_files) {
                ph->checker = ngx_http_core_try_files_phase;
                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--) { //in reversed order
            ph->checker = checker;
            ph->handler = h[j];  
            ph->next = n;
            ph++;
        }
    }

这段代码很简单,就是依次将每个阶段的handler放在cmcf->phase_engine.handlers中。

这里讲一下几点。首先可以看到switch语句中对于有些阶段会用break,有些阶段会用continue。用continue表示对于当前阶段不会走到后面的for循环,也就是说,不会将continue对应的阶段的handler函数加入到cmcf->phase_engine.handlers中。换句话说,Nginx不支持往这些阶段中添加handler函数。包括以下四个阶段

1.NGX_HTTP_FIND_CONFIG_PHASE
2.NGX_HTTP_POST_REWRITE_PHASE
3.NGX_HTTP_POST_ACCESS_PHASE
4.NGX_HTTP_TRY_FILES_PHASE

对应地在处理请求时,只执行它的checker函数。

我们知道cmcf->phase_engine.handlers数组每个成员的类型是:

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

从代码可以看到,next始终指向下一个阶段的第一个handler在cmcf->phase_engine.handlers中的位置。因此next提供了一种忽略当前阶段的其他handler直接进入下一阶段handler处理请求的方式:

r->phase_handler= next;

(注:每个阶段由多个handler组成,同一个阶段的所有handler在cmcf->phase_engine.handlers数组中出于连续的位置上)
而继续用当前阶段的handler处理则:

r->phase_handler++;

r->phase_handler一般是在各个checker函数中被改变的。而checker函数根据handler函数的返回结果改变这个值。
(注:相同阶段的所有handler对应的checker函数是一样的)。


总结

本文介绍了cmcf->phase_engine.handlers的初始化过程。cmcf->phase_engine.handlers是一个一维数组,它将所有阶段的handler存储起来,相同阶段的handler在cmcf->phase_engine.handlers中的位置相连续。在处理http请求时,依次调用cmcf->phase_engine.handlers中的handler(通过对应的checker调用handler),通过handler中的next来实现从一个阶段跳跃到下一个阶段的处理。处理请求时,主要是通过请求的r->phase_handler来标识这个请求当前被cmcf->phase_engine.handlers中的哪个handler处理。
这样实现了http请求的多阶段异步处理。

这里没有详细介绍各个阶段的功能,我们后面再详细介绍各个阶段在处理http请求中的具体作用。

猜你喜欢

转载自blog.csdn.net/swartz2015/article/details/78552029
今日推荐