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 ...
随机推荐
- POJ 1564 Sum It Up(DFS)
Sum It Up Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit St ...
- mvn多模块开发消除重复依赖造成的打包失败
错误信息: [ERROR] Failed to execute goal on project xiaoyiweifu-core: Could not resolve dependencies for ...
- JavaScriptSerializer类 对象序列化为JSON,JSON反序列化为对象
JavaScriptSerializer 类由异步通信层内部使用,用于序列化和反序列化在浏览器和 Web 服务器之间传递的数据.说白了就是能够直接将一个C#对象传送到前台页面成为javascript对 ...
- HttpWebRequest.Method 属性
public static void GetHead(string url) { var http = (HttpWebRequest)WebRequest.Create(url); http.Met ...
- cf472A Design Tutorial: Learn from Math
A. Design Tutorial: Learn from Math time limit per test 1 second memory limit per test 256 megabytes ...
- Unity 扩展属性自定义绘制
这么晚了准备睡觉的时候,去学习了一会. 发现一个标题好奇的点进去. 居然是自定义绘制属性. 在前几天这个问题把我难住了,没想到几分钟就能解决的问题. 我花了半天时间使用反射去解决... 如果我们想 ...
- NSNotificationCenter 传对象
[[NSNotificationCenter defaultCenter] postNotificationName:@"postCity" object:pro]; [[NSNo ...
- Spring的AOP2
本文是<AOP 那点事儿>的续集. 在上篇中,我们从写死代码,到使用代理:从编程式 Spring AOP 到声明式 Spring AOP.一切都朝着简单实用主义的方向在发展.沿着 Spri ...
- Bitmap工具类
一直在使用的一个Bitmap工具类 处理Bitmap和ImageView对象,实现了下面功能: 1.saveBitmap: 把Bitmap对象持久存储到SD卡或手机内存. 2.getViewBitma ...
- Ubuntu+Eclipse+ADT+Genymotion+VirtualBox开发环境搭建
1.Eclispe安装就不说了 2.以下说说怎样安装ADT插件.有两种途径: (1)在线安装: 地址:https://dl-ssl.google.com/android/eclipse/(只是近期天朝 ...