nginx 实际把 http 请求处理流程划分为了 11 个阶段, 这样划分的原因是将请求的执行逻辑细分, 以模块为单位进行处理, 各个阶段可以包含任意多个 HTTP 模块并以流水线的方式处理请求. 这样做的好处是使处理过程更加灵活, 降低耦合度. 这 11 个 HTTP 阶段如下所示:
1)NGX_HTTP_POST_READ_PHASE:
接收到完整的 HTTP 头部后处理的阶段, 它位于 uri 重写之前, 实际上很少有模块会注册在该阶段, 默认的情况下, 该阶段被跳过.
2)NGX_HTTP_SERVER_REWRITE_PHASE:
URI 与 location 匹配前, 修改 URI 的阶段, 用于重定向, 也就是该阶段执行处于 server 块内, location 块外的重写指令, 在读取请求头的过程中 nginx 会根据 host 及端口找到对应的虚拟主机配置.
3)NGX_HTTP_FIND_CONFIG_PHASE:
根据 URI 寻找匹配的 location 块配置项阶段, 该阶段使用重写之后的 uri 来查找对应的 location, 值得注意的是该阶段可能会被执行多次, 因为也可能有 location 级别的重写指令.
4)NGX_HTTP_REWRITE_PHASE:
上一阶段找到 location 块后再修改 URI,location 级别的 uri 重写阶段, 该阶段执行 location 基本的重写指令, 也可能会被执行多次.
5)NGX_HTTP_POST_REWRITE_PHASE:
防止重写 URL 后导致的死循环, location 级别重写的后一阶段, 用来检查上阶段是否有 uri 重写, 并根据结果跳转到合适的阶段.
6)NGX_HTTP_PREACCESS_PHASE:
下一阶段之前的准备, 访问权限控制的前一阶段, 该阶段在权限控制阶段之前, 一般也用于访问控制, 比如限制访问频率, 链接数等.
7)NGX_HTTP_ACCESS_PHASE:
让 HTTP 模块判断是否允许这个请求进入 Nginx 服务器, 访问权限控制阶段, 比如基于 ip 黑白名单的权限控制, 基于用户名密码的权限控制等.
8)NGX_HTTP_POST_ACCESS_PHASE:
访问权限控制的后一阶段, 该阶段根据权限控制阶段的执行结果进行相应处理, 向用户发送拒绝服务的错误码, 用来响应上一阶段的拒绝.
9)NGX_HTTP_TRY_FILES_PHASE:
为访问静态文件资源而设置, try_files 指令的处理阶段, 如果没有配置 try_files 指令, 则该阶段被跳过.
10)NGX_HTTP_CONTENT_PHASE:
处理 HTTP 请求内容的阶段, 大部分 HTTP 模块介入这个阶段, 内容生成阶段, 该阶段产生响应, 并发送到客户端.
11)NGX_HTTP_LOG_PHASE:
处理完请求后的日志记录阶段, 该阶段记录访问日志.
以上 11 个阶段中, HTTP 无法介入的阶段有 4 个:
- 3)NGX_HTTP_FIND_CONFIG_PHASE
- 5)NGX_HTTP_POST_REWRITE_PHASE
- 8)NGX_HTTP_POST_ACCESS_PHASE
- 9)NGX_HTTP_TRY_FILES_PHASE
剩余的 7 个阶段, HTTP 模块均能介入, 每个阶段可介入模块的个数也是没有限制的, 多个 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 lua 8 个阶段
- init_by_lua http
- set_by_lua server, server if, location, location if
- rewrite_by_lua http, server, location, location if
- access_by_lua http, server, location, location if
- content_by_lua location, location if
- header_filter_by_lua http, server, location, location if
- body_filter_by_lua http, server, location, location if
- log_by_lua http, server, location, location if
- 1)init_by_lua:
在 nginx 重新加载配置文件时, 运行里面 lua 脚本, 常用于全局变量的申请.(例如: lua_shared_dict 共享内存的申请, 只有当 nginx 重起后, 共享内存数据才清空, 这常用于统计.)
2)set_by_lua:
流程分支处理判断变量初始化(设置一个变量, 常用与计算一个逻辑, 然后返回结果, 该阶段不能运行 Output API,Control API,Subrequest API,Cosocket API)
3)rewrite_by_lua:
转发, 重定向, 缓存等功能 (例如特定请求代理到外网, 在 access 阶段前运行, 主要用于 rewrite)
4)access_by_lua:
IP 准入, 接口权限等情况集中处理(例如配合 iptable 完成简单防火墙, 主要用于访问控制, 能收集到大部分变量, 类似 status 需要在 log 阶段才有. 这条指令运行于 nginx access 阶段的末尾, 因此总是在 allow 和 deny 这样的指令之后运行, 虽然它们同属 access 阶段.)
5)content_by_lua:
内容生成, 阶段是所有请求处理阶段中最为重要的一个, 运行在这个阶段的配置指令一般都肩负着生成内容 (content) 并输出 HTTP 响应.
6)header_filter_by_lua:
应答 HTTP 过滤处理, 一般只用于设置 Cookie 和 Headers 等, 该阶段不能运行 Output API,Control API,Subrequest API,Cosocket API(例如添加头部信息).
7)body_filter_by_lua:
应答 BODY 过滤处理(例如完成应答内容统一成大写)(一般会在一次请求中被调用多次, 因为这是实现基于 HTTP 1.1 chunked 编码的所谓 "流式输出" 的, 该阶段不能运行 Output API,Control API,Subrequest API,Cosocket API)
8)log_by_lua:
会话完成后本地异步完成日志记录(日志可以记录在本地, 还可以同步到其他机器)(该阶段总是运行在请求结束的时候, 用于请求的后续操作, 如在共享内存中进行统计数据, 如果要高精确的数据统计, 应该使用 body_filter_by_lua, 该阶段不能运行 Output API,Control API,Subrequest API,Cosocket API)
三
nginx 和 lua 运行阶段的对应关系
1)init_by_lua, 运行在 initialization Phase;
2)set_by_lua, 运行在 rewrite 阶段;
set 指令来自 ngx_rewrite 模块, 运行于 rewrite 阶段;
3)rewrite_by_lua 指令来自 ngx_lua 模块, 运行于 rewrite 阶段的末尾
4)access_by_lua 指令同样来自 ngx_lua 模块, 运行于 access 阶段的末尾;
deny 指令来自 ngx_access 模块, 运行于 access 阶段;
5)content_by_lua 指令来自 ngx_lua 模块, 运行于 content 阶段; 不要将它和其它的内容处理指令在同一个 location 内使用如 proxy_pass;
echo 指令则来自 ngx_echo 模块, 运行在 content 阶段;
6)header_filter_by_lua 运行于 content 阶段, output-header-filter 一般用来设置 cookie 和 headers;
7)body_filter_by_lua, 运行于 content 阶段;
8)log_by_lua, 运行在 Log Phase 阶段;
如图:
来源: http://www.tuicool.com/articles/VzUvYbi