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

来源:互联网 发布:代码优化工具 编辑:程序博客网 时间:2024/05/20 16:41

前言

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函数:

voidngx_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 voidngx_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函数中完成:

voidngx_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_tngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf);static ngx_int_tngx_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_tngx_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_PHASE2. NGX_HTTP_SERVER_REWRITE_PHASE3. NGX_HTTP_REWRITE_PHASE4. NGX_HTTP_PREACCESS_PHASE5. NGX_HTTP_ACCESS_PHASE6. NGX_HTTP_CONTENT_PHASE7. 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_tngx_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_PHASE2.NGX_HTTP_POST_REWRITE_PHASE3.NGX_HTTP_POST_ACCESS_PHASE4.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请求中的具体作用。

原创粉丝点击