一个请求经过nginx处理的过程中,会经过一系列的阶段(phases),下面这个表格列出了nginx的所有phases,每个阶段可选的退出方式,包含的模块和对应的指令
phase | optional exits | modules / directives | description |
NGX_HTTP_POST_READ_PHASE | HttpRealIpModule | 读取请求内容阶段 | |
NGX_HTTP_SERVER_REWRITE_PHASE (server rewrite) | HttpRewriteModule / rewrite | 请求地址重写阶段 | |
NGX_HTTP_FIND_CONFIG_PHASE(location selection) | HttpCoreModule / location | 配置查找阶段 | |
NGX_HTTP_REWRITE_PHASE Location(location rewrite) | location selection, finalize request |
HttpRewriteModule / rewrite | 请求地址重写阶段 |
NGX_HTTP_POST_REWRITE_PHASE | HttpRewriteModule / rewrite | 请求地址重写提交阶段 | |
NGX_HTTP_PREACCESS_PHASE | degradation, NginxHttpLimitZoneModule / limit_zone, HttpLimitReqModule / limit req, HttpRealIpModule |
访问权限检查准备阶段 | |
NGX_HTTP_ACCESS_PHASE | finalize request | HttpAccessModule / allow, deny, NginxHttpAuthBasicModule / auth_basic | 访问权限检查阶段 |
NGX_HTTP_POST_ACCESS_PHASE | 访问权限检查提交阶段 | ||
NGX_HTTP_TRY_FILES_PHASE | location selection | HttpCoreModule / try_files | 配置项try_files处理阶段 |
NGX_HTTP_CONTENT_PHASE | HttpAutoindexModule / autoindex,HttpCoreModule / Core, HttpDavModule / DAV, HttpEmptyGifModule / EmptyGif, HttpFcgiModule / FastCGI, HttpFlvStreamModul / FLV, HttpGzipStaticModule / gzip_static, HttpIndexModule / index, HttpMemcachedModule / memcached, EmbeddedPerlModule / perl, HttpProxyModule / proxy, HttpProxyModule / random_index, HttpScgiModule / scgi, HttpStubStatusModule / stub_status, HttpUwsgiModule / uwsgi HttpLuaModule / content_by_lua, HttpCoreModule / proxy_pass |
内容产生阶段 | |
NGX_HTTP_LOG_PHASE | HttpLogModuel / access_log | 日志模块处理阶段 |
读完请求头后就进入了post_read 阶段,它位于uri被重写之前,这个阶段允许nginx改变请求头中ip地址的值,相关模块HttpRealIpModule.
这个阶段主要进行初始化全局变量,或者server级别的重写。如果把重写指令放到 server 中,那么就进入了server rewrite 阶段。(重写指令见rewrite phase)
这个阶段使用重写之后的uri来查找对应的location,值得注意的是该阶段可能会被执行多次,因为也可能有location级别的重写指令。
如果把重写指令放到 location中,那么就进入了rewrite phase,这个阶段是location级别的uri重写阶段,重写指令也可能会被执行多次;
重写指令有 HttpRewriteModule 的set指令,rewrite指令,HttpLuaModule的 set_by_lua指令, ngx_set_misc模块的set_unescape_uri指令,另外HttpRewriteModule的几乎所有指令都属于rewrite阶段。
到此,思考一个问题: 既然不用module的不同重写指令到可以在这个phase,那么这些指令是否可以在同一个location并存,如果可以,那么他们的执行顺序是怎么样的?
例子1 :
思考下面例子的输出的结果是什么?
location /test { set $a 32; set $b 56; set_by_lua $c "return ngx.var.a + ngx.var.b"; set $d "$a + $b = $c"; echo $d; }
当我们访问 http://localhost/test时,输出结果为 32 + 56 = 88 ,应该是和我们预期一致的。
但是这并不能证明所有属于同一个phases的不同的module的指令在一个phase中并存时一定是按照顺序执行下来的。 事实上,上面提到的这些第三方模块都采用了特殊的技术,将它们自己的配置指令“注入”到了 HttpRewriteModule的指令序列中(它们都借助了 Marcus Clyne 编写的第三方模块 ngx_devel_kit)。换句话说,更多常规的在 Nginx 的 rewrite 阶段注册和运行指令的第三方模块就没那么幸运了。这些“常规模块”的指令虽然也运行在 rewrite 阶段,但其配置指令和 HttpRewriteModule(以及同一阶段内的其他模块)都是分开独立执行的。在运行时,不同模块的配置指令集之间的先后顺序一般是不确定的(严格来说,一般是由模块的加载顺序决定的,但也有例外的情况)。比如 A 和 B 两个模块都在 rewrite 阶段运行指令,于是要么是 A 模块的所有指令全部执行完再执行 B 模块的那些指令,要么就是反过来,把 B 的指令全部执行完,再去运行 A 的指令。除非模块的文档中有明确的交待,否则用户一般不应编写依赖于此种不确定顺序的配置。还有不少第三方模块,ngx_array_var 以及用于加解密用户会话(session)的 ngx_encrypted_session,也都可以和HttpRewriteModule的指令无缝混合工作。标准 HttpRewriteModule的应用是如此广泛,所以能够和它的配置指令混合使用的第三方模块是幸运的。不能和HttpRewriteModule混合使用的指令在实际使用的过程要引起注意,它的输出是不确定的,这与他们在配置文件中的顺序无关。
结论:作用域为同一个phase的不同modules的指令,如果modules之间做了特殊的兼容,则它们按照指令在配置文件中出现的顺序依次执行下来
例子2:
思考下面这个输出什么?
location /test { set $value dog; more_set_input_headers "X-Species: $value"; set $value cat; echo "X-Species: $http_x_species"; }
说明:第三方模块 ngx_headers_more 提供了一系列配置指令,用于操纵当前请求的请求头和响应头。其中有一条名叫 more_set_input_headers 的指令可以在 rewrite 阶段改写指定的请求头(或者在请求头不存在时自动创建)。该指令的文档中有这么一行标记 phase: rewrite tail,是说这条指令总是运行在 rewrite 阶段的末尾。显然,写在
more_set_input_headers 指令之后的 set $value cat 语句却先执行了。也就是说属于HttpRewriteModule的set指令先执行完了,才执行ngx_header_more 的set_input_headers指令。
结论:即使运行在同一个请求处理阶段,分属不同模块的配置指令也可能会分开独立运行(除非像 ngx_set_misc 等模块那样针对 ngx_rewrite 模块提供特殊支持)。换句话说,在单个请求处理阶段内部,一般也会以 Nginx 模块为单位进一步地划分出内部子阶段。下面的例子3同例子2:
例子3:
location /test { set $a 1; rewrite_by_lua "ngx.var.a = ngx.var.a + 1"; set $a 56; echo $a; }
说明:HttpLuaModule的rewrite_by_lua 指令也是处在 rewrite tail phase,它也会在rewrite 阶段的末尾执行。因此HttpRewriteModule的所有set执行完后,才执行它。
显然,rewrite_by_lua 指令的行为不同于我们前面在 (二) 中介绍过的 set_by_lua 指令。
小伙伴们可能要问,既然 more_set_input_headers 和 rewrite_by_lua 指令都运行在 rewrite 阶段的末尾,那么它们之间的先后顺序又是怎样的呢?答案是:不一定。我们应当避免写出依赖它们二者间顺序的配置。
结论:作用域在同一个phase的不同modules的指令,如果没有做特殊兼容处理,则它们指令的执行顺序与指令在配置中出现的顺序无关,结果具有不确定性
location级别重写的下一阶段,用来检查上阶段是否有uri重写,并根据结果跳转到合适的阶段;
访问权限控制的前一阶段,该阶段在权限控制阶段之前,一般也用于访问控制,比如限制访问频率,链接数等;相关模块/指令 :NginxHttpLimitZoneModule / limit_zone,
HttpLimitReqModule / limit req, HttpRealIpModule
访问权限控制阶段,比如基于ip黑白名单的权限控制,基于用户名密码的认证控制等;相关模块/指令 HttpAccessModule / allow, deny, NginxHttpAuthBasicModule / auth_basic。 HttpAccessModule提供的 allow 和 deny 配置指令可用于控制哪些 IP 地址可以访问,哪些不可以。HttpAccessModule模块还支持所谓的“CIDR 记法”来表示一个网段,例如 169.200.179.4/24 则表示路由前缀是 169.200.179.0(或者说子网掩码是 255.255.255.0)的网段。
思考下下面两个例子(例子5,例子6)
例子4:
location /hello { allow 127.0.0.1; deny all; echo "hello world"; }
例子5:
location /hello { deny all; allow 127.0.0.1; echo "hello world"; }
例5和例6的区别在于deny all ,和allow 127.0.0.1 这两条指令的顺序不同。但例5中/hello 只允许从本机(IP 地址为保留的 127.0.0.1)访问,而从其他 IP 地址访问都会被拒(返回 403 错误页)。而例6中被配置为任何IP访问都会返回403错误。
原因说明:同属于HttpAccessModule这个模块的多条配置指令之间是按顺序执行的,直到遇到第一条满足条件的指令就不再执行后续的 allow 和 deny 指令。如果首先匹配的指令是 allow,则会继续执行后续其他模块的指令或者跳到后续的处理阶段;而如果首先满足的是 deny 则会立即中止当前整个请求的处理,并立即返回给客户端 403 错误页。
结论:同一个phase的同一个module内的多条指令的执行顺序由这个module自己来定义。
因为 HttpAccessModule的指令运行在 access 阶段,而 access 阶段又处于 rewrite 阶段之后,所以前面我们见到的所有那些在 rewrite 阶段运行的配置指令,都总是在 allow 和 deny 之前执行,而无论它们在配置文件中的书写顺序是怎样的。所以,为了避免阅读配置时的混乱,我们应该总是让指令的书写顺序和它们的实际执行顺序保持一致。
访问权限控制的后一阶段,该阶段根据权限控制阶段的执行结果进行相应处理;
HttpCoreModule的try_files指令的处理阶段,如果没有配置try_files指令,则该阶段被跳过; 该指令作用域: server ,location。
当try_files用于server阶段时一般是初始化作用,用来加载一些文件。
语法: try_files file1,file2,..,fileN-1 ... ,fallback 或者try_files file1,file2,..,fileN = code
用来顺序检查file1,file2,...fileN-1是否存在,如果最后一个字符为/表示这是一个目录。只要找到一个file存在,则进入到content phase,输出内容。如果前N-1个参数代表的file都不存在,此时最后一个参数发挥作用,最后一个参数用于内部跳转,并且只有最后一个参数是用作内部跳转,因此最后一个参数必须存在,否则将会引发一个内部错误。前面的file参数只是设置uri指向,不会引发内部跳转。此外注意: try_files和rewrite不同,rewrite指令会自动保存原请求的参数$args,而try_files最后的fallback参数如果需要带上请求的参数,则需要明确指出,如:
try_files $uri $uri/ /index.php?q=$uri&$args
内容生成阶段,该阶段产生响应,并发送到客户端; 这个阶段相关的模块和指令较多。这里仅拿HttpLuaModule的content_by_lua和HttpEchoModule的echo指令来举个例子再次证明上述谈论到的一个结论。
例子:
location /test { set $a 123; set $b 456; echo $b; content_by_lua ' ngx.say(ngx.var.a) '; }
location /test_echo { content_by_lua ' ngx.say(ngx.var.a) '; set $a 123; set $b 456; }
日志记录阶段,该阶段记录访问日志;
location /test { set $a 11; echo $a; set $a 22; echo $a; }
参考资料:
http://wiki.nginx.org/
http://wiki.nginx.org/Phases
http://blog.sina.com.cn/s/blog_6d579ff40100xpff.html
以上就介绍了nginx phases 介绍,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。