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 ...
随机推荐
- sql 数据库优化
数据库优化: 1. 显示磁盘秘密: DBCC SHOWCONTIG(B2B_ZRate) 清理磁盘密度 DBCC DBREINDEX(B2B_ZRate) 2.
- javascript加载优化
当加载js时,页面会一直在转,没有加载完. 如何优化这一点呢 js的加载方式介绍 同步加载---------------异步加载------------------延迟加载 异步加载仍然会触发wind ...
- ORA-14400: inserted partition key does not map to any partition
ORA-14400: inserted partition key does not map to any partition 数据库表已经分区,如果插入数据时出现错误提示: ORA-14400: 插 ...
- C语言必背18个经典程序
C语言必背18个经典程序 1./*输出9*9口诀.共9行9列,i控制行,j控制列.*/ #include "stdio.h" main() {int i,j,result; for ...
- Qt之HTTP上传/下载(继承QNetworkAccessManager,包括使用了authenticationRequired认证信号)
效果 QNetworkAccessManager DownloadNetworkManager::DownloadNetworkManager(QObject *parent) : QNetworkA ...
- mac安装GNU命令行工具
mac安装GNU命令行工具 2.添加的repo tap home/dupes brew install coreutils binutils diffutils ed -- ...
- Linux系统编程(13)——Shell的基本语法
按照惯例,Shell变量由全大写字母加下划线组成,有两种类型的Shell变量:环境变量和本地变量. 环境变量: 环境变量可以从父进程传给子进程,因此Shell进程的环境变量可以从当前Shell进程传给 ...
- 正则表达式小试牛刀--匹配我的csdn博文标题
正则表达式小试牛刀--匹配我的博文标题 作者:vpoet 邮箱:vpoet_sir@163.com 正则匹配,我以我的博客页面的博客标题为例:http://blog.csdn.net/u0130187 ...
- iOS openURL方法实现打电话、发短信、发邮件、打开其他App
UIApplication有个功能十分强大的openURL:方法 - (BOOL)openURL:(NSURL*)url; 通过这个方法,我们可以实现: 先获取 UIApplication UIApp ...
- cocos2d-x绑定ccb文件
首先,ccb文件是如何生成的,我就不多说了,大家可以搜下cocosbuilder,就能找的相关的教程,而通过cocosbuilder,我们可以省去了很多设计的麻烦,比如设计一个精灵的位置啥的,而通过c ...