【Nginx】HTTP請求的11個處理階段
阿新 • • 發佈:2018-11-26
Nginx將一個HTTP請求分成多個階段。以模組為單位進行處理。這樣做的優點是使處理過程更加靈活、減少耦合度。HTTP框架將處理分成了11個階段,各個階段能夠包括隨意多個HTTP模組並以流水線的方式處理請求。這11個HTTP階段例如以下所看到的:
typedef enum { NGX_HTTP_POST_READ_PHASE = 0, // 接收到完整的HTTP頭部後處理的階段 NGX_HTTP_SERVER_REWRITE_PHASE, // URI與location匹配前,改動URI的階段,用於重定向 NGX_HTTP_FIND_CONFIG_PHASE, // 依據URI尋找匹配的location塊配置項 NGX_HTTP_REWRITE_PHASE, // 上一階段找到location塊後再改動URI NGX_HTTP_POST_REWRITE_PHASE, // 防止重寫URL後導致的死迴圈 NGX_HTTP_PREACCESS_PHASE, // 下一階段之前的準備 NGX_HTTP_ACCESS_PHASE, // 讓HTTP模組推斷是否同意這個請求進入Nginx伺服器 NGX_HTTP_POST_ACCESS_PHASE, // 向用戶傳送拒絕服務的錯誤碼,用來響應上一階段的拒絕 NGX_HTTP_TRY_FILES_PHASE, // 為訪問靜態檔案資源而設定 NGX_HTTP_CONTENT_PHASE, // 處理HTTP請求內容的階段,大部分HTTP模組介入這個階段 NGX_HTTP_LOG_PHASE // 處理完請求後的日誌記錄階段 } ngx_http_phases;
以上11個階段中,HTTP無法介入的階段有4個:
- NGX_HTTP_FIND_CONFIG_PHASE
- NGX_HTTP_POST_REWRITE_PHASE
- NGX_HTTP_POST_ACCESS_PHASE
- NGX_HTTP_TRY_FILES_PHASE
以下是關於階段的一些定義:
typedef struct ngx_http_phase_handler_s ngx_http_phase_handler_t; typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r); struct ngx_http_phase_handler_s { ngx_http_phase_handler_pt checker; // 由HTTP框架定義和呼叫,此函式又呼叫下方的handler方法 ngx_http_handler_pt handler; // HTTP模組通過實現這種方法介入某個階段 ngx_uint_t next; // 下一個階段的序號 };
ngx_http_phase_handler_t結構體表示處理階段中的一個處理方法。當解析完http{}塊配置項後。會產生一個由ngx_http_phase_handler_t組成的陣列handlers,被放在了ngx_http_phase_engine_t結構體中:
typedef struct { ngx_http_phase_handler_t *handlers; // 一個請求可能經歷的全部ngx_http_handler_pt處理方法 ngx_uint_t server_rewrite_index; ngx_uint_t location_rewrite_index; } ngx_http_phase_engine_t;
handlers指向了這個陣列的起始地址。這個陣列是全部HTTP模組都能合作處理使用者請求的關鍵。 一個使用者請求就是被這個陣列中的ngx_http_handler_pt方法依次處理。
ngx_http_phase_engine_t結構體又被儲存在ngx_http_core_main_conf_t全域性結構體(由ngx_http_core_module模組的ngx_http_core_create_main_conf方法建立)中:
typedef struct {
....
ngx_http_phase_engine_t phase_engine; /* 儲存處理HTTP請求的各個階段 */
....
} ngx_http_core_main_conf_t;
在HTTP框架初始化過程中,不論什麼HTTP模組定義了處理請求的方法ngx_http_handler_pt後,都能夠呼叫自己的介面ngx_http_module_t中的postconfiguration函式將該方法加入到phase_engine中,也就是加入到ngx_http_core_main_conf_t.phase_engine.handlers陣列中。當一個HTTP請求到達時,Nginx會呼叫某階段的某個ngx_http_handler_pt指向的方法處理請求。介入全部11個階段均能夠使用上述方法。除此之外,介入NGX_HTTP_CONTENT_PHASE階段還能夠使用第二種方法:把希望處理請求的ngx_http_handler_pt方法設定到location配置塊相關的ngx_http_loc_conf_t結構體的handler指標中。例如以下所看到的:
static char* ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
// 找到mytest配置項所屬的配置塊
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
// 設定處理請求的方法,HTTP框架在處理使用者請求進行到NGX_HTTP_CONTENT_PHASE階段時
// 假設主機域名、URI和mytest模組所在配置塊名稱同樣。就會呼叫函式ngx_http_mytest_handler
clcf->handler = ngx_http_mytest_handler;
return NGX_CONF_OK;
}
如上例所看到的,它的優點在於:ngx_http_mytest_handler方法僅僅會處理和該location的URI相匹配的請求,不會處理其他請求。所以,應該依據ngx_http_handler_pt處理方法是處理全部HTTP請求還是處理特定的某個請求。使用不同的方法將ngx_http_handler_pt介入到NGX_HTTP_CONTENT_PHASE階段。
參考: 《深入理解Nginx》 P372-P382.