【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.
原文地址:https://www.cnblogs.com/ldxsuanfa/p/10020027.html