Nginx 配置指令的执行顺序(九)
紧接在 server-rewrite 阶段后边的是 find-config 阶段。这个阶段并不支持 Nginx 模块注册处理程序,而是由 Nginx 核心来完成当前请求与 location 配置块之间的配对工作。换句话说,在此阶段之前,请求并没有与任何 location 配置块相关联。因此,对于运行在 find-config 阶段之前的 post-read 和 server-rewrite 阶段来说,只有 server 配置块以及更外层作用域中的配置指令才会起作用。这就是为什么只有写在server 配置块中的 ngx_rewrite 模块的指令才会运行在 server-rewrite 阶段,这也是为什么前面所有例子中的 ngx_realip 模块的指令也都特意写在了 server 配置块中,以确保其注册在 post-read 阶段的处理程序能够生效。
当 Nginx 在 find-config 阶段成功匹配了一个 location 配置块后,会立即打印一条调试信息到错误日志文件中。我们来看这样的一个例子:
location /hello {
echo "hello world";
}
如果启用了 Nginx 的“调试日志”,那么当请求 /hello 接口时,便可以在 error.log 文件中过滤出下面这一行信息:
$ grep 'using config' logs/error.log
[debug] 84579#0: *1 using configuration "/hello"
我们有意省略了信息行首的时间戳,以便放在这里。
运行在 find-config 阶段之后的便是我们的老朋友 rewrite 阶段。由于 Nginx 已经在 find-config 阶段完成了当前请求与 location 的配对,所以从 rewrite 阶段开始,location 配置块中的指令便可以产生作用。前面已经介绍过,当 ngx_rewrite 模块的指令用于 location 块中时,便是运行在这个 rewrite 阶段。另外,ngx_set_misc 模块的指令也是如此,还有 ngx_lua 模块的 set_by_lua 指令和 rewrite_by_lua 指令也不例外。
rewrite 阶段再往后便是所谓的 post-rewrite 阶段。这个阶段也像 find-config 阶段那样不接受 Nginx 模块注册处理程序,而是由 Nginx 核心完成 rewrite 阶段所要求的“内部跳转”操作(如果 rewrite 阶段有此要求的话)。先前在 (二) 中已经介绍过了“内部跳转”的概念,同时演示了如何通过 echo_exec 指令或者 rewrite 指令来发起“内部跳转”。由于 echo_exec 指令运行在 content 阶段,与这里讨论的 post-rewrite 阶段无关,于是我们感兴趣的便只剩下运行在 rewrite 阶段的 rewrite 指令。回顾一下 (二) 中演示过的这个例子:
server {
listen 8080;
location /foo {
set $a hello;
rewrite ^ /bar;
}
location /bar {
echo "a = [$a]";
}
}
这里在 location /foo 中通过 rewrite 指令把当前请求的 URI 无条件地改写为 /bar,同时发起一个“内部跳转”,最终跳进了 location /bar 中。这里比较有趣的地方是“内部跳转”的工作原理。“内部跳转”本质上其实就是把当前的请求处理阶段强行倒退到 find-config 阶段,以便重新进行请求 URI 与 location 配置块的配对。比如上例中,运行在 rewrite 阶段的 rewrite 指令就让当前请求的处理阶段倒退回了 find-config 阶段。由于此时当前请求的 URI 已经被 rewrite 指令修改为了 /bar,所以这一次换成了 location /bar 与当前请求相关联,然后再接着从 rewrite 阶段往下执行。
不过这里更有趣的地方是,倒退回 find-config 阶段的动作并不是发生在 rewrite 阶段,而是发生在后面的 post-rewrite 阶段。上例中的 rewrite 指令只是简单地指示 Nginx 有必要在 post-rewrite 阶段发起“内部跳转”。这个设计对于 Nginx 初学者来说,或许显得有些古怪:“为什么不直接在 rewrite 指令执行时立即进行跳转呢?”答案其实很简单,那就是为了在最初匹配的 location 块中支持多次反复地改写 URI,例如:
location /foo {
rewrite ^ /bar;
rewrite ^ /baz;
echo foo;
}
location /bar {
echo bar;
}
location /baz {
echo baz;
}
这里在 location /foo 中连续把当前请求的 URI 改写了两遍:第一遍先无条件地改写为 /bar,第二遍再无条件地改写为 /baz. 而这两条 rewrite 语句只会最终导致 post-rewrite 阶段发生一次“内部跳转”操作,从而不至于在第一次改写 URI 时就直接跳离了当前的 location 而导致后面的 rewrite 语句没有机会执行。请求/foo 接口的结果证实了这一点:
$ curl localhost:8080/foo
baz
从输出结果可以看到,上例确实成功地从 /foo 一步跳到了 /baz 中。如果启用 Nginx “调试日志”的话,还可以从 find-config 阶段生成的 locatin 块的匹配信息中进一步证实这一点:
$ grep 'using config' logs/error.log
[debug] 89449#0: *1 using configuration "/foo"
[debug] 89449#0: *1 using configuration "/baz"
我们看到,对于该次请求,Nginx 一共只匹配过 /foo 和 /baz 这两个 location,从而只发生过一次“内部跳转”。
当然,如果在 server 配置块中直接使用 rewrite 配置指令对请求 URI 进行改写,则不会涉及“内部跳转”,因为此时 URI 改写发生在 server-rewrite 阶段,早于执行 location 配对的 find-config 阶段。比如下面这个例子:
这里,我们在 server-rewrite 阶段就把那些以 /foo 起始的 URI 改写为 /bar,而此时请求并没有和任何location 相关联,所以 Nginx 正常往下运行 find-config 阶段,完成最终的 location 匹配。如果我们请求上例中的 /foo 接口,那么 location /foo 根本就没有机会匹配,因为在第一次(也是唯一的一次)运行find-config 阶段时,当前请求的 URI 已经被改写为 /bar,从而只会匹配 location /bar. 实际请求的输出正是如此:
$ curl localhost:8080/foo
bar
Nginx “调试日志”可以再一次佐证我们的结论:
$ grep 'using config' logs/error.log
[debug] 92693#0: *1 using configuration "/bar"
可以看到,Nginx 总共只进行过一次 location 匹配,并无“内部跳转”发生。
Nginx 配置指令的执行顺序(九)的更多相关文章
- Nginx 配置指令的执行顺序(八)
前面我们详细讨论了 rewrite.access 和 content 这三个最为常见的 Nginx 请求处理阶段,在此过程中,也顺便介绍了运行在这三个阶段的众多 Nginx 模块及其配置指令.同时可以 ...
- Nginx 配置指令的执行顺序(五)
Nginx 的 content 阶段是所有请求处理阶段中最为重要的一个,因为运行在这个阶段的配置指令一般都肩负着生成“内容”(content)并输出 HTTP 响应的使命.正因为其重要性,这个阶段的配 ...
- Nginx 配置指令的执行顺序(一)
大多数 Nginx 新手都会频繁遇到这样一个困惑,那就是当同一个 location 配置块使用了多个 Nginx 模块的配置指令时,这些指令的执行顺序很可能会跟它们的书写顺序大相径庭.于是许多人选择了 ...
- Nginx配置指令的执行顺序
rewrite阶段 rewrite阶段是一个比较早的请求处理阶段,这个阶段的配置指令一般用来对当前请求进行各种修改(比如对URI和URL参数进行改写),或者创建并初始化一系列后续处理阶段可能需要的Ng ...
- Nginx 配置指令的执行顺序(十)
运行在 post-rewrite 阶段之后的是所谓的 preaccess 阶段.该阶段在 access 阶段之前执行,故名preaccess. 标准模块 ngx_limit_req 和 ngx_lim ...
- Nginx 配置指令的执行顺序(六)
前面我们在 (五) 中提到,在一个 location 中使用 content 阶段指令时,通常情况下就是对应的 Nginx 模块注册该 location 中的“内容处理程序”.那么当一个 locati ...
- Nginx 配置指令的执行顺序(三)
如前文所述,除非像 ngx_set_misc 模块那样使用特殊技术,其他模块的配置指令即使是在 rewrite 阶段运行,也不能和 ngx_rewrite 模块的指令混合使用.不妨来看几个这样的例子. ...
- Nginx 配置指令的执行顺序(二)
我们前面已经知道,当 set 指令用在 location 配置块中时,都是在当前请求的 rewrite 阶段运行的.事实上,在此上下文中,ngx_rewrite 模块中的几乎全部指令,都运行在 rew ...
- Nginx 配置指令的执行顺序
在一个 location 中使用 content 阶段指令时,通常情况下就是对应的 Nginx 模块注册该 location 中的“内容处理程序”.那么当一个 location 中未使用任何 cont ...
随机推荐
- Wireless Network(POJ 2236)
Wireless Network Time Limit: 10000MS Memory Limit: 65536K Total Submissions: 20724 Accepted: 871 ...
- Yslow-23条规则编辑
1. 减少HTTP请求次数 合并图片.CSS.JS,改进首次访问用户等待时间. 2. 使用CDN 就近缓存==>智能路由==>负载均衡==>WSA全站动态加速 3. 避免空的 ...
- 用来控制 XML 序列化的属性
通过将下表中的属性应用于类和类成员,可以控制 XmlSerializer 序列化或反序列化该类的实例的方式.若要了解这些属性如何控制 XML 序列化,请参见使用属性控制 XML 序列化. 这些属性还可 ...
- wireshark----教你如何抓包
wireshark----教你如何抓包 wireshark是一款强大的抓包工具,走过路过一定不要错过就是了,当你学习TCP/IP协议的时候,学习使用wireshark 抓包正是理论联系实际最好的方法, ...
- 【POJ 1330 Nearest Common Ancestors】LCA问题 Tarjan算法
题目链接:http://poj.org/problem?id=1330 题意:给定一个n个节点的有根树,以及树中的两个节点u,v,求u,v的最近公共祖先. 数据范围:n [2, 10000] 思路:从 ...
- hibernate多对一的操作解析
在hibernate的关联操作中有很多关系,其中多对一关系是最常见的.我们看这两个表. 这里有部门表和员工表. 那么我们可以这么说一个部门可以有多个员工.这就是1对多的关系.这是我们站在部门表的角度上 ...
- U-Boot 启动过程和源码分析(第二阶段)-main_loop分析
1> main_loop common/main.c /******************************************************************** ...
- 分享几种Linux软件的安装方法
Linux软件安装由于不同的Linux分支,安装方法也互不相同,介绍几种常见的安装方法. 1. 源码安装, 对于本身具有开源血统的Linux系统来说,几乎所有的开源软件都支持在Linux平台运 ...
- Unity Scene为每一个游戏物体进行扩展编辑
2个月前还在忙碌的找实习工作,看见招聘信息上面有一条熟悉扩展Unity编辑器,配合美工编程. 自己动手写完这个代码时候,发现写代码就像弹钢琴多么神奇. TestEdit类: using UnityEn ...
- m元素集合的n个元素子集
理论: 假设有5个元素的集点,取出3个元素的可能子集如下:{1 2 3}.{1 2 4 }.{1 2 5}.{1 3 4}.{1 3 5}.{1 4 5}.{2 3 4}.{2 3 5}.{2 4 5 ...