1. 程式人生 > >nginx phase handler的原理和選擇

nginx phase handler的原理和選擇

nginx phase handler的原理和選擇
PHASE HANDLER的種類
nginx在接收並解析完請求行,請求頭之後,就會依次呼叫各個phase handler。 phase handler是完成nginx主要功能的階段。

Nginx有如下11種phase,phase會依次執行。同一個phase,可能會掛載多個handler。其中斜體加粗的phase,不允許掛載使用者自定義的handler

PHASE    備註
NGX_HTTP_POST_READ_PHASE    讀取請求內容階段
NGX_HTTP_SERVER_REWRITE_PHASE    Server請求地址重寫階段
NGX_HTTP_FIND_CONFIG_PHASE    配置查詢階段
NGX_HTTP_REWRITE_PHASE    Location請求地址重寫階段
NGX_HTTP_POST_REWRITE_PHASE    請求地址重寫提交階段
NGX_HTTP_PREACCESS_PHASE    訪問許可權檢查準備階段
NGX_HTTP_ACCESS_PHASE    訪問許可權檢查階段
NGX_HTTP_POST_ACCESS_PHASE    訪問許可權檢查提交階段
NGX_HTTP_TRY_FILES_PHASE    配置項try_files處理階段
NGX_HTTP_CONTENT_PHASE    內容產生階段
NGX_HTTP_LOG_PHASE    日誌模組處理階段
如何註冊phase handler
一般情況下,我們自定義的模組,大多數是掛載在NGX_HTTP_CONTENT_PHASE階段的。掛載的動作一般是在模組上下文呼叫的postconfiguration函式中。

掛載的程式碼示例如下:

static ngx_int_t
ngx_http_hello_init(ngx_conf_t *cf)    // postconfiguration hook點
{
        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_CONTENT_PHASE].handlers);
        if (h == NULL) {
                return NGX_ERROR;
        }
 
        *h = ngx_http_hello_handler;
 
        return NGX_OK;
}

cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers 是content phase階段的handler陣列,類似的:

cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers 
cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers
cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers
。。。
每個可掛載的phase,都有一個phase handler陣列, 你可以選擇掛載在不同的phase數組裡

Nginx在解析http的配置時,會將多個phase handler陣列,合成為一個數組

static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  // http指令的hook函式
{
    。。。 
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }
 
        module = ngx_modules[m]->ctx;
 
        if (module->postconfiguration) {
            if (module->postconfiguration(cf) != NGX_OK) {// 執行各模組的postconfigure hook, phase handler就在此時掛載
                return NGX_CONF_ERROR;
            }
        }
  }
  。。。
  
  if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {// 將多個phase handler陣列,合成為一個數組
      return NGX_CONF_ERROR;
  }
   。。。
}
ngx_http_init_phase_handlers(cf, cmcf) 函式將(除log phase以外的)幾個phase handler陣列,合成為一個ngx_http_phase_handler_t型別的陣列

struct ngx_http_phase_handler_s {
    ngx_http_phase_handler_pt  checker; // 檢查handler返回結果,決定下一個執行的handler
    ngx_http_handler_pt        handler; // handler 鉤子
    ngx_uint_t                 next;    // 指向下一個phase的第一個handler
};
以下是不同的phase,對應不同的checker函式

PHASE    checker
NGX_HTTP_POST_READ_PHASE    ngx_http_core_generic_phase
NGX_HTTP_SERVER_REWRITE_PHASE    ngx_http_core_rewrite_phase
NGX_HTTP_FIND_CONFIG_PHASE    ngx_http_core_find_config_phase
NGX_HTTP_REWRITE_PHASE    ngx_http_core_rewrite_phase
NGX_HTTP_POST_REWRITE_PHASE    ngx_http_core_post_rewrite_phase
NGX_HTTP_PREACCESS_PHASE    ngx_http_core_generic_phase
NGX_HTTP_ACCESS_PHASE    ngx_http_core_access_phase
NGX_HTTP_POST_ACCESS_PHASE    ngx_http_core_post_access_phase
NGX_HTTP_TRY_FILES_PHASE    ngx_http_core_try_files_phase
NGX_HTTP_CONTENT_PHASE    ngx_http_core_content_phase
NGX_HTTP_LOG_PHASE    ngx_http_core_generic_phase
Phase handler如何執行
nginx在接收並解析完請求行,請求頭之後,就會依次呼叫各個phase handler.

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;
        }
    }
}
可以看到,其實依次執行的就是我們在初始化階段,生成的ngx_http_phase_handler_t 陣列

我們通過debug,來看一下ngx_http_phase_handler_t陣列的實際內容

執行中時ph陣列的內容
陣列下標    checker    handler    next
0    ngx_http_core_rewrite_phase    ngx_http_rewrite_handler    1
1    ngx_http_core_find_config_phase    0    0
2    ngx_http_core_rewrite_phase    ngx_http_rewrite_handler    3
3    ngx_http_core_post_rewrite_phase    0    1
4    ngx_http_core_generic_phase    ngx_http_l7waf_handler    7
5    ngx_http_core_generic_phase    ngx_http_limit_req_handler    7
6    ngx_http_core_generic_phase    ngx_http_limit_conn_handler    7
7    ngx_http_core_access_phase    ngx_http_access_handler    10
8    ngx_http_core_access_phase    ngx_http_auth_basic_handler    10
9    ngx_http_core_post_access_phase    0    10
10    ngx_http_core_try_files_phase    0    0
11    ngx_http_core_content_phase    ngx_http_index_handler    14
12    ngx_http_core_content_phase    ngx_http_autoindex_handler    14
13    ngx_http_core_content_phase    ngx_http_static_handler    14
14    0    0    0
我們來看一個具體的checker函式

ngx_int_t
ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
    ngx_int_t  rc;
 
    /*
     * generic phase checker,
     * used by the post read and pre-access phases
     */
 
    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "generic phase: %ui", r->phase_handler);
 
    rc = ph->handler(r);          // 執行掛載的handler
 
    if (rc == NGX_OK) {           // 返回值是NGX_OK,說明這個phase執行完了,跳到下一個phase的第一個handler去
        r->phase_handler = ph->next;
        return NGX_AGAIN;
    }
 
    if (rc == NGX_DECLINED) {     // 返回值NGX_DECLINED,繼續執行這個phase的下一個handler
        r->phase_handler++;
        return NGX_AGAIN;
    }
 
    if (rc == NGX_AGAIN || rc == NGX_DONE) { //說明請求處理完了。下面所有的phase handler都不需要再執行了
        return NGX_OK;
    }
 
    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */
 
    ngx_http_finalize_request(r, rc);        // 返回錯誤,結束請求,返回相應的錯誤頁
 
    return NGX_OK;
}
 
如何選擇哪個phase
讀取請求內容階段

這個階段沒有預設的handler,主要用來讀取請求體,並對請求體做相應的處理

Server請求地址重寫階段

這個階段主要是處理全域性的(server block)的rewrite規則

配置查詢階段

這個階段主要是通過uri來查詢對應的location。然後將uri和location的資料關聯起來。這個階段主要處理邏輯在checker函式中,不能掛載自定義的handler

Location請求地址重寫階段

這個主要處理location block的rewrite。

請求地址重寫提交階段

post rewrite,這個主要是進行一些校驗以及收尾工作,以便於交給後面的模組。這個phase不能掛載自定義handler

訪問許可權檢查準備階段

比如流控這種型別的access就放在這個phase,也就是說它主要是進行一些比較粗粒度的access。

訪問許可權檢查階段

這個比如存取控制,許可權驗證就放在這個phase,一般來說處理動作是交給下面的模組做的.這個主要是做一些細粒度的access。

訪問許可權檢查提交階段

一般來說當上面的access模組得到access_code之後就會由這個模組根據access_code來進行操作 這個phase不能掛載自定義handler

配置項try_files處理階段

try_file模組,也就是對應配置檔案中的try_files指令。 這個phase不能掛載自定義handler

按順序檢查檔案是否存在,返回第一個找到的檔案。結尾的斜線表示為資料夾 -$uri/。如果所有的檔案都找不到,會進行一個內部重定向到最後一個引數。

內容產生階段

內容處理模組,產生檔案內容,如果是php,去呼叫phpcgi,如果是代理,就轉發給相應的後端伺服器

日誌模組處理階段

日誌處理模組,是每個請求最後一定會執行的。用於列印訪問日誌。

通過如上對phase handler的分析,我們可以知道nginx劃分不同的phase,是將不同功能,安排在不同的順序執行。

選擇掛載在哪個phase,就選擇了handler執行的順序,並且選擇了不同的checker函式。

自定義的handler有時候可以掛載在不同的phase,都可以正常執行。

自定義的handler,如果依賴某一個phase的結果,則必須掛載在該phase後面的phase上。

自定義的handler需要遵守nginx對不同phase的功能劃分,但不是必需的。

除去4個不能掛載的phase,和log phase,還有如下6個phase可以掛載

NGX_HTTP_POST_READ_PHASE
NGX_HTTP_SERVER_REWRITE_PHASE
NGX_HTTP_REWRITE_PHASE
NGX_HTTP_PREACCESS_PHASE
NGX_HTTP_ACCESS_PHASE
NGX_HTTP_CONTENT_PHASE

很多功能掛載在這6個phase,都可以實現。掛載在越前面,我們的效能會越好,掛載在後面,我們的自由度會更大。

比如說,如果我們實現在NGX_HTTP_POST_READ_PHASE 階段,我們就不能用 location if 這些後面階段實現的指令來組合實現一些更復雜的功能。

推薦使用
NGX_HTTP_PREACCESS_PHASE
NGX_HTTP_ACCESS_PHASE
NGX_HTTP_CONTENT_PHASE
--------------------- 
作者:囧囧有神 
來源:CSDN 
原文:https://blog.csdn.net/liujiyong7/article/details/38817135 
版權宣告:本文為博主原創文章,轉載請附上博文連結!