NGINX中HTTP請求的11個處理階段
Nginx的模組化設計使得每一個HTTP模組可以僅專注於完成一個獨立的、簡單的功能,而一個請求的完整處理過程可以由無數個HTTP模組共同合作完成。這種設計有非常好的簡單性、可測試性、可擴充套件性,然而,當多個http模組流水式地處理同一個請求時,單一的處理順序是無法滿足靈活性需求的,每一個正在處理的HTTP模組很難靈活、有效地指定下一個HTTP處理模組是哪一個。
因此,HTTP框架依據常見的處理流程將處理階段劃分為11個階段,其中每個處理階段都可以由任意多個HTTP模組流水式地處理請求。
typedef enum { //在接收到完整的HTTP頭部後處理的HTTP階段 NGX_HTTP_POST_READ_PHASE = 0, //在請求的URI與location表示式匹配前,修改請求的URI(重定向),是一個獨立的HTTP階段 NGX_HTTP_SERVER_REWRITE_PHASE, //根據請求的URI尋找匹配的location表示式,這個階段只能由 NGX_HTTP_FIND_CONFIG_PHASE, //在NGX_HTTP_FIND_CONFIG_PHASE階段尋找到匹配的location之後再修改請求的URI NGX_HTTP_REWRITE_PHASE, //這一階段是用於在rewrite重寫URL後,防止錯誤的nginx.conf配置導致死迴圈(遞迴地修改URI) NGX_HTTP_POST_REWRITE_PHASE, //表示在處理NGX_HTTP_ACCESS_PHASE階段覺得請求的訪問許可權前,HTTP模組可以介入的處理階段 NGX_HTTP_PREACCESS_PHASE, //這個階段用於讓HTTP模組判斷是否允許這個請求訪問nginx伺服器 NGX_HTTP_ACCESS_PHASE, //在NGX_HTTP_ACCESS_PHASE階段中,當HTTP模組的handler處理函式返回不允許訪問的錯誤碼時(NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUHORIZED),這裡將負責向用戶傳送拒絕服務的錯誤響應。 NGX_HTTP_POST_ACCESS_PHASE, //此階段專門為try_files配置項設立,當HTTP請求訪問靜態檔案資源時,try_files配置項可以使這個配置項順序地訪問多個靜態檔案資源 NGX_HTTP_TRY_FILES_PHASE, //用於處理HTTP請求內容的階段,這是大部分HTTP模組最願意介入的階段 NGX_HTTP_CONTENT_PHASE, //處理完請求後記錄日誌的階段,ngx_http_log_module模組就在這個階段中加入了一個handler處理方法,使得每個HTTP請求處理完畢後會記錄access_log訪問日誌 NGX_HTTP_LOG_PHASE } ngx_http_phases;
這11個階段有些是必備的,有些是可選的,也可以有多個HTTP模組同時介入同一階段
HTTP階段的定義,包括checker檢查方法和handler處理方法,如下所示:
typedef structngx_http_phase_handler_s ngx_http_phase_handler_t; /*一個HTTP處理階段中的checker檢查方法,僅可以由HTTP框架實現,以此控制HTTP請求的處理流程*/ typedef ngx_int_t(*ngx_http_phase_handler_pt)(ngx_http_request_t *r, ngx_http_phase_handler_t*ph); /*由HTTP模組實現的handler處理方法*/ typedef ngx_int_t(*ngx_http_handler_pt)(ngx_http_request_t *r); struct ngx_http_phase_handler_s { /*在處理到某一個HTTP階段時,HTTP框架將會在checker方法已實現的前提下首先呼叫checker方法來處理請求, 而不會直接呼叫任何階段中的hanlder方法,只有在checker方法中才會去呼叫handler方法,因此,事實上所有 的checker方法都是由框架中的ngx_http_core_module模組實現的,且普通模組無法重定義checker方法*/ ngx_http_phase_handler_pt checker; /*除ngx_http_core_module模組以外的HTTP模組,只能通過定義handler方法才能介入某一個HTTP處理階段以處理請求*/ ngx_http_handler_pt handler; /*將要處理的下一個HTTP處理階段的序號 next的設計使得處理階段不必按順序依次執行,既可以向後跳躍數個階段繼續執行,也可以跳躍到之前的某個階段重新 執行,通常,next表示下一個處理階段中的第1個ngx_http_phase_handler_t處理方法*/ ngx_uint_t next; };
一個http{}塊解析完畢後,將會根據nginx.conf中的配置產生由ngx_http_phase_handler_t組成的陣列,在處理HTTP請求時,一般情況下這些階段是順序向後執行的,但ngx_http_phase_handler_t中的next成員使得它們也可以非順序地執行,ngx_http_phase_engine_t結構體就是所有ngx_http_phase_handler_t組成的陣列,如下所示:
typedef struct { /*handlers是由ngx_http_phase_handler_t構成的陣列首地址,它表示一個請求可能經歷的所有ngx_http_handler_pt處理方法*/ ngx_http_phase_handler_t *handlers; /*表示NGX_HTTP_SERVER_REWRITE_PHASE階段第1個ngx_http_phase_handler_t處理方法在handlers陣列中的序號,用於在執行 HTTP請求的任何階段中快速跳轉到HTTP_SERVER_REWRITE_PHASE階段處理請求*/ ngx_uint_t server_rewrite_index; /*表示NGX_HTTP_PREACCESS_PHASE階段第1個ngx_http_phase_handler_t處理方法在handlers陣列中的序號,用於在執行 HTTP請求的任何階段中快速跳轉到NGX_HTTP_PREACCESS_PHASE階段處理請求*/ ngx_uint_t location_rewrite_index; } ngx_http_phase_engine_t;
可以看到,ngx_http_phase_engine_t中儲存了在當前nginx.conf配置下,一個使用者請求可能經歷的所有ngx_http_handler_pt處理方法,這是所有HTTP模組可以合作處理使用者請求的關鍵,這個ngx_http_phase_engine_t結構體儲存在全域性的ngx_http_core_main_conf_t結構體中,如下:
typedef struct {
ngx_array_t servers; /* ngx_http_core_srv_conf_t */
/*由下面各階段處理方法構成的phases陣列構建的階段引擎才是流水式處理HTTP請求的實際資料結構*/
ngx_http_phase_engine_t phase_engine;
ngx_hash_t headers_in_hash;
ngx_hash_t variables_hash;
ngx_array_t variables; /* ngx_http_variable_t */
ngx_uint_t ncaptures;
ngx_uint_t server_names_hash_max_size;
ngx_uint_t server_names_hash_bucket_size;
ngx_uint_t variables_hash_max_size;
ngx_uint_t variables_hash_bucket_size;
ngx_hash_keys_arrays_t *variables_keys;
ngx_array_t *ports;
ngx_uint_t try_files; /* unsigned try_files:1 */
/*用於在HTTP框架初始化時幫助各個HTTP模組在任意階段中新增HTTP處理方法,它是一個有11個成員的ngx_http_phase_t陣列,
其中每一個ngx_http_phase_t結構體對應一個HTTP階段,在HTTP框架初始化完畢後,執行過程中的phases陣列是無用的*/
ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;
在ngx_http_phase_t中關於HTTP階段有兩個成員:phase_engine和phases,其中phase_engine控制執行過程中的一個HTTP請求所要經過的HTTP處理階段,它將配合ngx_http_request_t結構體中的phase_handler成員使用(phase_handler制定了當前請求應當執行哪一個HTTP階段);而phases陣列更像一個臨時變數,它實際上僅會在Nginx啟動過程中用到,它的唯一使命是按照11個階段的概率初始化phase_engine中的handlers陣列
typedef struct {
/*handlers動態陣列儲存著每一個HTTP模組初始化時新增到當前階段的處理方法*/
ngx_array_t handlers;
} ngx_http_phase_t;
在HTTP框架的初始化過程中,任何HTTP模組都可以在ngx_http_module_t介面的postconfiguration方法中將自定義的方法新增到handler動態陣列中,這樣,這個方法就會最終新增到phase_engine動態陣列中。
參考:《深入理解Nginx模組開發與架構設計》 陶輝 機械工業出版社