1. 程式人生 > >【Nginx】HTTP請求的11個處理階段

【Nginx】HTTP請求的11個處理階段

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
剩餘的7個階段。HTTP模組均能介入。每一個階段可介入模組的個數也是沒有限制的,多個HTTP模組可同一時候介入同一階段並作用於同一請求。
以下是關於階段的一些定義:
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.