nginx源碼分析——http多階段處理

1. 多階段處理概述

nginx將一個http請求分爲順序的多個處理階段,前一個階段的結果會影響後一個階段的處理。例如,ngx_http_access_module模塊根據IP信息拒絕一個用戶請求後,本應接着執行的其餘HTTP模塊將沒有機會再處理這個請求。nginx

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模塊介入進行請求的處理。post

  • 各處理模塊將處理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數組狀況大體以下圖所示:ui

 

  • 初始化多階段處理引擎
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數組的關係:spa

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》

相關文章
相關標籤/搜索