0.1:变量生成阶段
0.2:内容输出阶段(content后包括content阶段)
e.g.
单set,单echo
现在就来看这样一个令人困惑的例子:
? location /test { ? set $a 32; ? echo $a; ? ? set $a 56; ? echo $a; ? }
从这个例子的本意来看,我们期望的输出是一行 32
和一行 56
,因为我们第一次用 echo 配置指令输出了 $a
变量的值以后,又紧接着使用 set 配置指令修改了 $a
. 然而不幸的是,事实并非如此:
$ curl 'http://localhost:8080/test 56 56
我们看到,语句 set $a 56
似乎在第一条 echo $a
语句之前就执行过了。这究竟是为什么呢?难道我们遇到了 Nginx 中的一个 bug?
显然,这里并没有 Nginx 的 bug;要理解这里发生的事情,就首先需要知道 Nginx 处理每一个用户请求时,都是按照若干个不同阶段(phase)依次处理的。
Nginx 的请求处理阶段共有 11 个之多,我们先介绍其中 3 个比较常见的。按照它们执行时的先后顺序,依次是 rewrite
阶段、access
阶段以及 content
阶段(后面我们还有机会见到其他更多的处理阶段)。
现在我们就不会再为前面那个例子输出两行一模一样的 56
而感到惊讶了。我们根本没有机会在第二条 set 语句之前用 echo 输出。幸运的是,仍然可以借助一些小技巧来达到最初的目的:
location /test { set $a 32; set $saved_a $a; set $a 56; echo $saved_a; echo $a; }
此时的输出便符合那个问题示例的初衷了:
$ curl 'http://localhost:8080/test' 32 56
这里通过引入新的用户变量 $saved_a
,在改写 $a
之前及时保存了 $a
的初始值。而对于多条 set 指令而言,它们之间的执行顺序是由 ngx_rewrite 模块来保证与书写顺序相一致的。同理,ngx_echo 模块自身也会保证它的多条 echo 指令之间的执行顺序。
细心的读者应当发现,我们在 Nginx 变量漫谈系列 的示例中已经广泛使用了这种技巧,来绕过因处理阶段而引起的指令执行顺序上的限制。
看到这里,有的读者可能会问:“那么我在使用一条陌生的配置指令之前,如何知道它究竟运行在哪一个处理阶段呢?”答案是:查看该指令的文档(当然,高级开发人员也可以直接查看模块的 C 源码)。在许多模块的文档中,都会专门标记其配置指令所运行的具体阶段。例如 echo 指令的文档中有这么一行:
phase: content
这一行便是说,当前配置指令运行在 content
阶段。如果你使用的 Nginx 模块碰巧没有指示运行阶段的文档,可以直接联系该模块的作者请求补充。不过,值得一提的是,并非所有的配置指令都与某个处理阶段相关联,例如我们先前在 Nginx 变量漫谈(一) 中提到过的 geo 指令以及在 Nginx 变量漫谈(四) 中介绍过的 map 指令。这些不与处理阶段相关联的配置指令基本上都是“声明性的”(declarative),即不直接产生某种动作或者过程。Nginx 的作者 Igor Sysoev 在公开场合曾不止一次地强调,Nginx 配置文件所使用的语言本质上是“声明性的”,而非“过程性的”(procedural)。
NGX_HTTP_POST_READ_PHASE:
接收完请求头之后的第一个阶段,它位于uri重写之前,实际上很少有模块会注册在该阶段,默认的情况下,该阶段被跳过
NGX_HTTP_SERVER_REWRITE_PHASE:
server级别的uri重写阶段,也就是该阶段执行处于server块内,location块外的重写指令,在读取请求头的过程中nginx会根据host及端口找到对应的虚拟主机配置
NGX_HTTP_FIND_CONFIG_PHASE:
寻找location配置阶段,该阶段使用重写之后的uri来查找对应的location,值得注意的是该阶段可能会被执行多次,因为也可能有location级别的重写指令
NGX_HTTP_REWRITE_PHASE:
location级别的uri重写阶段,该阶段执行location基本的重写指令,也可能会被执行多次
NGX_HTTP_POST_REWRITE_PHASE:
location级别重写的后一阶段,用来检查上阶段是否有uri重写,并根据结果跳转到合适的阶段
NGX_HTTP_PREACCESS_PHASE:
访问权限控制的前一阶段,该阶段在权限控制阶段之前,一般也用于访问控制,比如限制访问频率,链接数等
NGX_HTTP_ACCESS_PHASE:
访问权限控制阶段,比如基于ip黑白名单的权限控制,基于用户名密码的权限控制等
NGX_HTTP_POST_ACCESS_PHASE:
问权限控制的后一阶段,该阶段根据权限控制阶段的执行结果进行相应处理
NGX_HTTP_TRY_FILES_PHASE:
try_files指令的处理阶段,如果没有配置try_files指令,则该阶段被跳过
NGX_HTTP_CONTENT_PHASE:
内容生成阶段,该阶段产生响应,并发送到客户端
NGX_HTTP_LOG_PHASE:
日志记录阶段,该阶段记录访问日志
---------------------
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
{
set_by_lua: 流程分支处理判断变量初始化
rewrite_by_lua: 转发、重定向、缓存等功能(例如特定请求代理到外网)
access_by_lua: IP准入、接口权限等情况集中处理(例如配合iptable完成简单防火墙)
content_by_lua: 内容生成
header_filter_by_lua: 应答HTTP过滤处理(例如添加头部信息)
body_filter_by_lua: 应答BODY过滤处理(例如完成应答内容统一成大写)
log_by_lua: 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其他机器)
}
参考文献:
https://blog.csdn.net/yangguanghaozi/article/details/54139258
http://blog.sina.com.cn/s/blog_6d579ff40100xm7t.html
https://www.jianshu.com/p/6f7e41b81985
https://juejin.im/post/5bf671a4e51d4546d60a9bd1