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?
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
總結
以上是生活随笔為你收集整理的nginx phase handler的原理和选择的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gitlab project项目迁移
- 下一篇: Nginx入门之两种handler函数的