转载自 http://blog.sina.com.cn/openresty
大多数 Nginx 新手都会频繁遇到这样一个困惑,那就是当同一个 location 配置块使用了多个 Nginx 模块的配置指令时,这些指令的执行顺序很可能会跟它们的书写顺序大相径庭。于是许多人选择了“试错法”,然后他们的配置文件就时常被改得一片狼藉。这个系列的教程就旨在帮助读者逐步地理解这些配置指令背后的执行时间和先后顺序的奥秘。
现在就来看这样一个令人困惑的例子:
? 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 阶段(后面我们还有机会见到其他更多的处理阶段)。
所有 Nginx 模块提供的配置指令一般只会注册并运行在其中的某一个处理阶段。比如上例中的 set 指令就是在 rewrite 阶段运行的,而 echo 指令就只会在 content 阶段运行。前面我们已经知道,在单个请求的处理过程中,rewrite 阶段总是在 content 阶段之前执行,因此属于 rewrite 阶段的配置指令也总是会无条件地在 content 阶段的配置指令之前执行。于是在同一个 location 配置块中,set 指令总是会在 echo 指令之前执行,即使我们在配置文件中有意把 set 语句写在 echo 语句的后面。
回到刚才那个例子,
set $a 32;
echo $a;
set $a 56;
echo $a;
实际的执行顺序应当是
set $a 32;
set $a 56;
echo $a;
echo $a;
即先在 rewrite 阶段执行完这里的两条 set 赋值语句,然后再在后面的 content 阶段依次执行那两条 echo 语句。分属两个不同处理阶段的配置指令之间是不能穿插着运行的。
为了进一步验证这一点,我们不妨借助 Nginx 的“调试日志”来一窥 Nginx 的实际执行过程。
因为这是我们第一次提及 Nginx 的“调试日志”,所以有必要先简单介绍一下它的启用方法。调试日志默认是禁用的,因为它会引入比较大的运行时开销,让 Nginx 服务器显著变慢。一般我们需要重新编译和构造 Nginx 可执行文件,并且在调用 Nginx 源码包提供的 ./configure 脚本时传入 --with-debug 命令行选项。例如我们下载完 Nginx 源码包后在 Linux 或者 Mac OS X 等系统上构建时,典型的步骤是这样的:
tar xvf nginx-1.0.10.tar.gz
cd nginx-1.0.10/
./configure --with-debug
make
sudu make install
如果你使用的是我维护的 ngx_openresty 软件包,则同样可以向它的 ./configure 脚本传递 --with-debug 命令行选项。
当我们启用 --with-debug 选项重新构建好调试版的 Nginx 之后,还需要同时在配置文件中通过标准的 error_log 配置指令为错误日志使用 debug 日志级别(这同时也是最低的日志级别):
error_log logs/error.log debug;
这里重要的是 error_log 指令的第二个参数,debug,而前面第一个参数是错误日志文件的路径,logs/error.log. 当然,你也可以指定其他路径,但后面我们会检查这个文件的内容,所以请特别留意一下这里实际配置的文件路径。
现在我们重新启动 Nginx(注意,如果 Nginx 可执行文件也被更新过,仅仅让 Nginx 重新加载配置是不够的,需要关闭再启动 Nginx 主服务进程),然后再请求一下我们刚才那个示例接口:
$ curl 'http://localhost:8080/test'
56
56
现在可以检查一下前面配置的 Nginx 错误日志文件中的输出。因为文件中的输出比较多(在我的机器上有 700 多行),所以不妨用 grep 命令在终端上过滤出我们感兴趣的部分:
grep -E 'http (output filter|script (set|value))' logs/error.log
在我机器上的输出是这个样子的(为了方便呈现,这里对 grep 命令的实际输出作了一些简单的编辑,略去了每一行的行首时间戳):
[debug] 5363#0: *1 http script value: "32"
[debug] 5363#0: *1 http script set $a
[debug] 5363#0: *1 http script value: "56"
[debug] 5363#0: *1 http script set $a
[debug] 5363#0: *1 http output filter "/test?"
[debug] 5363#0: *1 http output filter "/test?"
[debug] 5363#0: *1 http output filter "/test?"
这里需要稍微解释一下这些调试信息的具体含义。set 配置指令在实际运行时会打印出两行以 http script 起始的调试信息,其中第一行信息是 set 语句中被赋予的值,而第二行则是 set 语句中被赋值的 Nginx 变量名。于是上面首先过滤出来的
[debug] 5363#0: *1 http script value: "32"
[debug] 5363#0: *1 http script set $a
这两行就对应我们例子中的配置语句
set $a 32;
而接下来这两行调试信息
[debug] 5363#0: *1 http script value: "56"
[debug] 5363#0: *1 http script set $a
则对应配置语句
set $a 56;
此外,凡在 Nginx 中输出响应体数据时,都会调用 Nginx 的所谓“输出过滤器”(output filter),我们一直在使用的 echo 指令自然也不例外。而一旦调用 Nginx 的“输出过滤器”,便会产生类似下面这样的调试信息:
[debug] 5363#0: *1 http output filter "/test?"
当然,这里的 "/test?" 部分对于其他接口可能会发生变化,因为它显示的是当前请求的 URI. 这样联系起来看,就不难发现,上例中的那两条 set 语句确实都是在那两条 echo 语句之前执行的。
细心的读者可能会问,为什么这个例子明明只使用了两条 echo 语句进行输出,但却有三行 http output filter 调试信息呢?其实,前两行 http output filter 信息确实分别对应那两条 echo 语句,而最后那一行信息则是对应 ngx_echo 模块输出指示响应体末尾的结束标记。正是为了输出这个特殊的结束标记,才会多出一次对 Nginx “输出过滤器”的调用。包括 ngx_proxy 在内的许多模块在输出响应体数据流时都具有此种行为。
现在我们就不会再为前面那个例子输出两行一模一样的 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)。
相关推荐
2. **执行Rewrite规则**:在找到的location块内,按照配置文件中的顺序逐个执行`rewrite`指令。每次重写都会重新检查location匹配,因为新的URL可能与之前的不匹配。 3. **last标志的处理**:如果`rewrite`指令后面...
2. **选择处理模块**:Nginx基于配置文件中的location指令,选择合适的处理模块来处理请求。 3. **负载均衡**:如果配置了负载均衡,负载均衡模块会根据策略选择一个后端服务器。 4. **处理请求**:处理模块执行其...
- **try_files**: 按顺序查找文件,找到即返回,未找到则按顺序执行下一条指令。 ### 3. Nginx反向代理 通过`proxy_pass`指令,Nginx可以作为反向代理服务器转发请求到后端应用服务器,实现负载均衡、缓存等功能。...
其配置文件是Nginx的核心部分,它决定了Nginx如何响应请求和处理网络流量。本篇文章将深入探讨Linux环境下Nginx的配置文件细节。 **一、Nginx配置文件结构** Nginx的配置文件通常命名为`nginx.conf`,位于 `/etc/...
标题中的"NGINX实现一个域名访问多个项目1"是指利用Nginx服务器的配置能力,让同一个域名能够根据不同的URL路径指向不同的应用或项目。描述中提到,这是为了解决在一个域名下部署多个项目的问题,避免为每个项目单独...
Nginx 是一款高性能的 Web 服务器和反向代理服务器,被广泛用于处理静态内容、动态内容分发以及负载均衡。本指南将深入探讨 Nginx 的负载均衡实现原理及其配置方法。 **一、Nginx 负载均衡原理** Nginx 的负载均衡...
理解Nginx指令的执行顺序对于高效配置Nginx至关重要。正确的指令顺序可以显著提高服务器性能并减少不必要的资源浪费。本系列详细介绍了Nginx指令的执行逻辑及最佳实践: 1. **基本概念**:首先概述了Nginx指令的...
配置指令的执行顺序也会影响最终的Nginx行为。 由于Nginx的配置涉及多方面的知识,因此教程的连载计划一般会从基础开始,逐步深入到高级主题。例如,在“Nginx新手起步”中,可能涵盖了最基础的安装和配置流程,而...
**Nginx配置Upstream负载均衡详解** 在现代Web服务架构中,负载均衡是一项至关重要的技术,它能够有效地分散网络流量,确保服务器集群的稳定性和高可用性。Nginx作为一款高性能的反向代理服务器和HTTP缓存服务器,...
#### Nginx 配置指令执行顺序 - **系列内容**:这一系列包含十一篇文章,详细解析Nginx配置指令的执行逻辑。 - **重要性**:了解配置指令的执行顺序有助于更好地管理配置文件。 - **涵盖知识点**: - **指令解析**...
负载均衡是Nginx的一个重要功能,它可以将客户端的请求分发到多个服务器上,从而提高系统的处理能力和扩展性。下面详细解释Nginx负载均衡的配置方法以及负载均衡策略。 首先,要配置Nginx与Tomcat实现负载均衡,...
3. **指令执行顺序**:Nginx在处理请求时,会按照以下顺序执行配置指令: - 首先执行全局块中的指令。 - 然后是events块内的指令。 - 接着是http块中的指令。 - 最后根据请求的端口找到对应的server块,并执行...
一个基本的Nginx负载均衡配置通常包含以下部分: 1. **upstream块**:定义一组后端服务器,并设置负载均衡策略。 2. **server块**:在http、server或location上下文中,引用upstream块,并配置相应的反向代理规则。...
本文将详细解析Nginx的配置选项,特别是`listen`和`location`指令,以及它们在创建虚拟主机和处理请求中的作用。 首先,我们来看`listen`指令。`listen`用于指定服务器监听的地址和端口,其语法如下: ```nginx ...
教程内容包括但不限于Nginx变量使用、配置指令执行顺序、子请求处理、静态文件服务、日志服务以及与其他服务(如Memcached、Redis、MySQL、PostgreSQL等)的集成。该教程还涉及了安全和访问控制的相关内容。 该教程...
这里,`location /` 是匹配所有请求的默认规则,`try_files` 指令用于指定文件查找顺序。对于.php文件,Nginx会将请求转发给PHP解析器处理,这里假设是通过fastcgi协议与PHP-FPM通信。 接下来是PHP的安装和配置。...
Nginx的配置文件中包含多个指令,其中Location指令是用于处理特定URI请求的关键部分。本文将详细介绍Nginx Location指令的基本语法、匹配过程、配置实例以及Nginx中的全局变量。 首先,Location指令的基本语法非常...