上一篇主要介绍Nginx是如何处理HTTP Header。由于HTTP请求处理这部分代码是Nginx核心内容,打算用两篇文章深入介绍。本篇先介绍HTTP处理的11个阶段。
一、HTTP处理11阶段
1.1、为什么要有11阶段?
这个得从Nginx设计出发。首先Nginx是单线程且完全异步框架,Nginx把所有功能都统一抽象成模块,即“万物皆模块”。每个模块仅仅完成一个独立、简单的功能(说白点,就是一个功能模块不能占用cpu时间太长)。对于一个HTTP请求来说,需要我们考虑的内容很多,因此Nginx将HTTP请求划分成11个阶段,每个阶段只处理很简单的功能,例如:ACCESS访问权限。
1.2、11阶段定义
Nginx使用枚举定义11个阶段,具体每个阶段的含义已经在注释中表明。(注释摘自《深入理解Nginx模块开发与架构解析》)
typedef enum {
/**
* 在接收到完整的HTTP头部后处理的HTTP阶段
*/
NGX_HTTP_POST_READ_PHASE = 0,
/**
* 在将请求的URI与location表达式匹配前,修改请求的URI(所谓的重定向)是一个独立的HTTP阶段
*/
NGX_HTTP_SERVER_REWRITE_PHASE,
/**
* 根据请求的URI寻找匹配的location表达式,这个阶段只能由ngx_http_core_module模块实现,
* 不建议其他HTTP模块重新定义这一阶段的行为
*/
NGX_HTTP_FIND_CONFIG_PHASE,
/**
* 在NGX_HTTP_FIND_CONFIG_PHASE阶段寻找到匹配的location之后再修改请求的URI
*/
NGX_HTTP_REWRITE_PHASE,
/**
* 这一阶段是用于在rewrite重写URL后,防止错误的nginx.conf配置导致死循环(递归地修改URI),
* 因此,这一阶段仅由ngx_http_core_module模块处理。目前,控制死循环的方式很简单,首先检查
* rewrite的次数,如果一个请求超过10次重定向,就认为进入了rewrite死循环,这时在
* NGX_HTTP_POST_REWRITE_PHASE阶段就会向用户返回500,表示服务器内部错误
*/
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_UNAUTHORIZED),这里将负责向用户发送拒绝服务的错误响应。因此,
* 这个阶段实际上用于给NGX_HTTP_ACCESS_PHASE阶段收尾
*/
NGX_HTTP_POST_ACCESS_PHASE,
/**
* 这个阶段完全是为try_files配置项而设立的,当HTTP请求访问静态文件资源时,
* try_files配置项可以使这个请求顺序地访问多个静态文件资源,如果某一次访问失败,则继续访问
* try_files中指定的下一个静态资源。这个功能完全是在
* NGX_HTTP_TRY_FILES_PHASE阶段中实现的
*/
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;
1.3、流水线处理
既然Nginx设计了11个阶段,那么Nginx是如何管理的呢?对于流程化管理,目前业界有两种比较好的方式:状态机和流水线。Nginx采用流水线方式(Netty也采用流水线方式)。那么Nginx具体是如何实现的呢?
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);
/**
* http阶段 流水线处理
*/
struct ngx_http_phase_handler_s {
ngx_http_phase_handler_pt checker; /* 当前阶段checker函数 */
ngx_http_handler_pt handler; /* 当前阶段处理函数 在checker函数中调用 */
ngx_uint_t next; /* 下一处理阶段 */
};
说明:
1、checker函数只能由Nginx框架实现,用户不能修改。
2、handler函数,可以由用户设置,handler回调函数只能在checker函数中调用。
3、next指定下一个阶段序号。Nginx虽然指定了11阶段,但是它允许跳跃执行。例如:当ACCESS阶段校验不通过,可直接结束当前流程,而不必继续向下执行。
4、checker回调函数设置在函数ngx_http_init_phase_handlers中。
1.4、如何介入
虽然Nginx定义了11个阶段,但并不是每一个阶段用户自定义模块都可以进入,具体可参考下面:
名称 | 备注 | |
用户模块可介入 | 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 NGX_HTTP_LOG_PHASE |
用户自定义的模块,可以介入这些模块。其中NGX_HTTP_CONTENT_PHASE是最常见介入阶段。 |
用户模块不可介入 | NGX_HTTP_FIND_CONFIG_PHASE NGX_HTTP_POST_REWIRTE_PHASE NGX_HTTP_POST_ACESS_PHASE NGX_HTTP_TRY_FILES_PHASE |
用户自定义模块不可以介入,这部分代码只能有HTTP框架介入。 |
那么如何介入呢?Nginx有两种方式:
1、设置postconfiguration回调函数,向全局的ngx_http_core_main_conf_t结构体的phase[NGX_HTTP_xx_PHASE]动态数组中添加ngx_http_handler_pt处理方法。例如ACCESS模块添加:
static ngx_int_t
ngx_http_access_init(ngx_conf_t *cf)
{
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_ACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_access_handler;
return NGX_OK;
}
2、设置ngx_http_core_loc_conf_t结构中handler回调函数。Nginx知道大部分都是介入NGX_HTTP_CONTENT_PHASE阶段,为了方便,Nginx在ngx_http_core_loc_conf_t中增了一个回调函数。而且这种方式是NGX_HTTP_CONTENT_PHASE阶段独有的。若仍然不清楚,可参考本篇《菜鸟学习Nginx之入门开发留言板》中函数ngx_http_webservice_login。
两种方式,第一种方式适用于所有阶段,而第二种方式仅适用于NGX_HTTP_CONTENT_PHASE阶段。
二、总结
本篇只简单介绍Nginx为了解决复杂的HTTP请求,定义了11个阶段以及如何介入11个阶段。下一篇正式介绍HTTP请求如何处理。