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
版權宣告:本文為博主原創文章,轉載請附上博文連結!