1. 多阶段处理概述

nginx将一个http请求分为顺序的多个处理阶段,前一个阶段的结果会影响后一个阶段的处理。例如,ngx_http_access_module模块根据IP信息拒绝一个用户请求后,本应接着执行的其他HTTP模块将没有机会再处理这个请求。

nginx之所以要把http请求的处理过程分为多个阶段,是因为nginx的模块化设计使得每一个http模块可以仅专注于完成一个独立的、简单的功能,而一个请求的完整处理过程可以由无数个HTTP模块共同合作完成。这种设计有非常好的简单性、可测试性、可扩展性,然而,当多个HTTP模块流水式地处理同一个请求时,单一的顺序是无法满足灵活性需求的,每一个正在处理请求的HTTP模块很难灵活、有效地指定下一个HTTP处理模块时哪一个。而且,不划分处理阶段也会让HTTP请求的完整处理流程难以管理,每一个HTTP模块也很难正确的将自己插入到完整流程的合适位置中。

nginx依据常见的处理流程将处理阶段划分为11个阶段,其中每个处理阶段都可以由任意多个HTTP模块流水式地处理请求。对于这11个处理阶段,有些阶段是必备的,有些阶段是可选的,当然也可以有多个HTTP模块同时介入同一处理阶段。



2. 多阶段处理相关的结构体

  • 阶段的枚举定义
typedef enum {
    // 读请求内容阶段
    NGX_HTTP_POST_READ_PHASE = 0,
    // server请求地址重写阶段
    NGX_HTTP_SERVER_REWRITE_PHASE,
    // 配置查找阶段
    NGX_HTTP_FIND_CONFIG_PHASE,
    // location请求地址重写阶段
    NGX_HTTP_REWRITE_PHASE,
    // 请求地址重写提交阶段
    NGX_POST_REWRITE_PHASE,
    // 访问权限检查准备阶段
    NGX_HTTP_PREACCESS_PHASE,
    // 访问权限检查阶段
    NGX_HTTP_ACCESS_PHASE,
    // 访问权限检查提交阶段
    NGX_HTTP_POST_ACCESS_PHASE,
    // 配置项try_files处理阶段
    NGX_HTTP_TRY_FILES_PHASE,
    // 内容产生阶段
    NGX_HTTP_CONTENT_PHASE,
    // 日志模块处理阶段
    NGX_HTTP_LOG_PHASE
} ngx_http_phases ;
  • 多阶段处理的定义
// handler处理函数定义
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_requet_t *r);
// checker检查函数定义
typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r, ngx_http_phase_handler_t *ph);

typedef struct ngx_http_phase_handler_s ngx_http_phase_handler_t;

struct ngx_http_phase_handler_s {
    // 在处理到某一个HTTP阶段时, HTTP框架将会在checker方法已实现的前提下首先调用checker方法来处理请求
    // 而不会直接调用任何阶段中的handler方法, 只有在checker方法中才会去调用handler方法
    // 事实上所有的checker方法都是由框架中的ngx_http_core_module模块实现的, 且普通的HTTP模块无法重定义checker方法
    ngx_http_phase_handler_pt    checker;
    // 除 ngx_http_core_module模块意外的HTTP模块, 只能通过定义handler方法才能介入某一个HTTP处理阶段来处理请求
    ngx_http_handler_pt          handler;
    // 将要执行的下一个HTTP处理阶段的序号
    ngx_uint_t                   next;
};
  • http多阶段处理引擎
typedef struct {
    // handlers是由ngx_http_phase_handler_t构成的数组首地址
    // 它表示一个请求可能经历的所有ngx_http_handler_pt处理方法
    ngx_http_phase_handler_t  *handlers;
    ngx_uint_t                server_rewrite_index;
    ngx_uint_t                location_rewrite_index;
} ngx_http_phase_engine_t;

http多阶段处理引擎作为http main配置项结构体的成员变量,驱动http多阶段的处理。在main配置项结构体中海油另外一个成员变量phases,它是一个数组的数组,按不同的处理阶段存储了所有的handler。



3. 多阶段初始化

  • 内存空间分配

    在http配置的解析过程中,首先会初始化phases数组,分配内存空间用于存储所有处理阶段的handler。

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

 注意,这里仅对7个处理阶段分配了内存空间,NGX_HTTP_FIND_CONFIG_PHASE,NGX_HTTP_POST_REWRITE_PHASE,NGX_HTTP_POST_ACCESS_PHASE,NGX_HTTP_TRY_FILES_PHASES这四个阶段没有分配内存空间,这是因为这4个处理阶段仅由nginx自身的框架实现,不允许HTTP模块介入进行请求的处理。

  • 各处理模块将处理handler添加到phases数组中

    在http配置解析过程中,调用http模块的postconfiguration方法,在该方法中,各模块将处理handler添加到main配置项结构体的phases数组中。

static ngx_http_module_t ngx_http_realip_module_ctx = {
    ngx_http_realip_add_variables,
    ngx_http_realip_init,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_realip_create_loc_conf,
    ngx_http_realip_merge_loc_conf
};

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

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

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

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

    return NGX_OK;
}

添加完成后phases数组情况大致如下图所示:

 

  • 初始化多阶段处理引擎
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_t  *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].handler.nelts ? 1 : 0;
    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;

    // NGX_HTTP_REWRITE_PHASE, NGX_HTTP_ACCESS_PHASE, NGX_HTTP_TRY_FILES_PHASE, NGX_HTTP_FIND_CONFIG_PHASE四个阶段的handler个数
    n = use_rewrite + use_access + cmcf->try_files + 1;

    // 其他几个阶段存储在phases数组中的handler个数
    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;
    n = 0;

    // 设置每个阶段的checker方法,以及处理的handler
    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_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--) {
            ph->checker = checker;
            ph->handler = h[j];
            ph->next = n;
            ph++;
        }
    }

    return NGX_OK;    
}

多阶段处理引擎与phases数组的关系:



4. 多阶段的处理流程

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

在checker方法中,通过增加phase_handler计数,或者通过phase_handler的next成员变量,可以达到多个处理模块顺序处理,或者根据执行结果跳转指定模块处理的方式。


参考:

《深入理解nginx》