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 ...
随机推荐
- 推荐大家一本学习php模式的书
对我来讲,写程序不是码代码,不想只是简单的将类拿来调用,然后功能实现了,可是以后要做一些扩展或者是修改就要对代码大刀阔斧. 在网站的开发过程中,使用一些框架,团队就可以在一定的程度上,分工合作.但是当 ...
- SQL Serverf 索引 - 索引压缩 、附加特性 <第十篇>
一.索引压缩 数据和索引压缩在SQL Server2008被引入.压缩一个索引意味着将在一个页面中获得更多的关键字信息.这可以造成重大的性能改进,因为存储索引需要的页面和索引级别更少.因为索引中的键值 ...
- cf471B MUH and Important Things
B. MUH and Important Things time limit per test 1 second memory limit per test 256 megabytes input s ...
- .NET中栈和堆的比较 #1
原文出处:http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory01122006130034PM/csharp_memory.a ...
- 内存映射与DMA
1.mmap系统调用的实现过程,该系统调用直接将设备内存映射到用户进程的地址空间. 2.用户空间内存如何映射到内核中(get_user_pages). 3.直接内存访问(DMA),他使得外设具有直接访 ...
- 使用ObjectInputStream的readObject()方法如何判断读取到多个对象的结尾
摘自http://blog.csdn.net/fjdingsd/article/details/46765803 使用ObjectInputStream的readObject()方法如何判断读取到多个 ...
- 【转】android camera(四):camera 驱动 GT2005
关键词:android camera CMM 模组 camera参数 GT2005 摄像头常见问题 平台信息: 内核:linux系统:android 平台:S5PV310(samsung exyn ...
- sqlite数据库方言配置
1. application.properties配置sqlite数据库 spring.datasource.url = jdbc:sqlite:C:/test/sqlite/DB/sqlite.db ...
- Ice_cream's world I
Ice_cream's world I Time Limit : 3000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) ...
- 多线程中的lua同步问题
最近写paintsnow::start时出现了一个非常麻烦的BUG,程序的Release版本大约每运行十几次就会有一次启动时崩溃(Debug版本还没崩溃过),崩溃点也不固定.经过简单分析之后,确定是线 ...