Nginx源码分析之 HTTP阶段

typedef enum {
    /*最开始的阶段,貌似Nginx原生没有该阶段的handler*/
    NGX_HTTP_POST_READ_PHASE = 0,

    /*配置在server块的rewrite指令,则会处理hander,否则,handler进去直接return*/
    NGX_HTTP_SERVER_REWRITE_PHASE,

    /*根据URI进行location块查找*/
    NGX_HTTP_FIND_CONFIG_PHASE,

    /*对URI进行修改*/
    NGX_HTTP_REWRITE_PHASE,

    /*决定是跳回NGX_HTTP_FIND_CONFIG_PHASE还是继续下一个handler,如果URI被rewrite了,则跳回去*/
    NGX_HTTP_POST_REWRITE_PHASE,

    /*下面三个都是访问控制,不了解*/
    NGX_HTTP_PREACCESS_PHASE,

    NGX_HTTP_ACCESS_PHASE,
    NGX_HTTP_POST_ACCESS_PHASE,

    NGX_HTTP_TRY_FILES_PHASE,

    /*content阶段,常用,例如proxy_pass指令等都在这里触发*/
    NGX_HTTP_CONTENT_PHASE,

    /关闭r的时候,会调用,用于access打日志*/
    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

一开始以为Nginx的所谓的阶段和Netfilter各个钩子一样,嵌入在整个转发流程的各个地方(local_in,ip_forward等),后来发现理解错了,Nginx的阶段是在读取HTTP全部请求首部后,在一个地方统一跑了一遍(log阶段除外)。
ngx_http_core_run_phases 函数负责处理所有可能处理的阶段(log阶段除外)。

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;
        }
    }
}

上面代码可见,Nginx把所有的handler放在一个数组里面:cmcf->phase_engine.handlers,数组每个元素是ngx_http_phase_handler_t结构体,每个结构体包含1个handler和1个checker,所有ngx_http_phase_handler_t包含了所有phase的所有handler(或者checker)。

checker主要作用是判断handler的返回值,然后依据返回值决定下一步怎么处理,例如接着处理下一个hander(checker返回NGX_AGAIN),或者处理完毕,不需要走下去了(checker返回NGX_OK)。所以我们编写模块handler的时候,需要特别注意自己的返回值。

下面是配置解析阶段最后,将各模块的 handler 变成数组。

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 */;

    /*计算所有注册phase的handler个数*/
    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        n += cmcf->phases[i].handlers.nelts;
    }

    /*为这些handler分配空间*/
    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;
    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后,所有该阶段的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++;
            /*不通过break处理,表示该阶段只有checker,没有handler,且只有一个*/

            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;

        /*把phases[i]阶段的handler按逆序排列,next指向的是下一个phase的第一个handler*/
        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;
}

同一个phase内,checker一样,handler不一样。如下图这种形式,phase0有3个handler,但是checker是一样的。

phase = 0         phase = 1               phase = 2 
--------------------------------------------------------
| h1 | h2  | h3  | h4  | h5  | h6  | h7  | h8  | h9  | 
--------------------------------------------------------

next 用来跳到另一个phase(根据自己phase逻辑处理,注意这里的措词,是另一个phase,而不是下一个phase)。

例如 在 rewrite阶段改变了url,且没有配置break,则会重新跳回find config阶段(在checker为ngx_http_core_post_rewrite_phase中设置了r->phase_handler = ph->next)。反正想跳到哪里,设置r->phase_handler就对了。r->phase_handler可以简单的++表示处理下一个hander。也可以指定成next,next具体指,完全看自己业务逻辑。

猜你喜欢

转载自blog.csdn.net/mrpre/article/details/81001677