一個請求經過nginx處理的過程中,會經過一系列的階段(phases),下面這個表格列出了nginx的所有phases,每個階段可選的退出方式,包含的模組和對應的指令
phase | optional exits | modules / pRealIpModule | 讀取請求內容階段 |
NGX_HTTP_SERVER_REWRITE_PHASE (server rewrite) | HttpRewriteModule / rewrite | 請求地址重寫階段 | |
NGX_HTTP_FIND_CONFIG_PHASE(lo | 配置查找階段NGX_HTTP_REWRITE_PHASE Location(location rewrite) | location 。重寫提交階段 | |
NGX_HTTP_PREACCESS_PHASE | degradation, NginxHttpLimitZoneModule / limit_zone, | HttpLimitReqModule / limit req, HttpRealIpModule | |
NGX_HTTP_c/c面AuthBasicModule / auth_basic | 存取權檢查階段NGX_HTTP_POST_ACCESS_PHASE | ||
存取權限檢查提交階段 | NGX_HTTP_TRY_FILES_PHASE | ||
HttpNGCoreModule / 處理階段_CONTENT_PHASE |
HttpAutoindexModule / autoindex,HttpCoreModule / Core, HttpDavModule / DAV, | HttpEmptyGifModule / EmptyGif, HttpFcgiModule / FastCGI, HttpFlvStreamModul / FLV, HttpGzipStaticModule / gzip_static, HttpIndem / perl, | HttpProxyModule / proxy, HttpProxyModule / random_index,|
HttpCoreModule / proxy_pass | 內容產生階段 | NGX_HTTP_LOGP_HThTPSm&Mr8m al | |
讀完請求頭後就進入了post_read 階段,它位於uri被重寫之前,這個階段允許nginx改變請求頭中就進入了post_read 階段,它位於uri被重寫之前,這個階段允許nginx改變請求頭中的值,相關模組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"; } 访问:curl 'http://localhost/test' 输出:X-Species: cat 是不是和你的预期有点儿不一样呢? 说明:第三方模块 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指令。 例子3: location /test { set $a 1; rewrite_by_lua "ngx.var.a = ngx.var.a + 1"; set $a 56; echo $a; } 访问: curl 'http://localhost/test' 输出: 57 说明:HttpLuaModule的rewrite_by_lua 指令也是处在 rewrite tail phase,它也会在rewrite 阶段的末尾执行。因此HttpRewriteModule的所有set执行完后,才执行它。 结论:作用域在同一个phase的不同modules的指令,如果没有做特殊兼容处理,则它们指令的执行顺序与指令在配置中出现的顺序无关,结果具有不确定性 location级别重写的下一阶段,用来检查上阶段是否有uri重写,并根据结果跳转到合适的阶段; 访问权限控制的前一阶段,该阶段在权限控制阶段之前,一般也用于访问控制,比如限制访问频率,链接数等;相关模块/指令 :NginxHttpLimitZoneModule / limit_zone, 访问权限控制阶段,比如基于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) location /hello { allow 127.0.0.1; deny all; echo "hello world"; } 本机访问:curl http://localhost/hello ,输出:hello world 其他机器访问:curl http://localhost/hello ,报403错误 例子5: location /hello { deny all; allow 127.0.0.1; echo "hello world"; } 本机访问:curl http://localhost/hello ,报403错误 其他机器访问:curl http://localhost/hello ,报403错误 例5和例6的区别在于deny all ,和allow 127.0.0.1 这两条指令的顺序不同。但例5中/hello 只允许从本机(IP 地址为保留的 127.0.0.1)访问,而从其他 IP 地址访问都会被拒(返回 403 错误页)。而例6中被配置为任何IP访问都会返回403错误。 结论:同一个phase的同一个module内的多条指令的执行顺序由这个module自己来定义。 因为 HttpAccessModule的指令运行在 access 阶段,而 access 阶段又处于 rewrite 阶段之后,所以前面我们见到的所有那些在 rewrite 阶段运行的配置指令,都总是在 allow 和 deny 之前执行,而无论它们在配置文件中的书写顺序是怎样的。所以,为了避免阅读配置时的混乱,我们应该总是让指令的书写顺序和它们的实际执行顺序保持一致。 访问权限控制的后一阶段,该阶段根据权限控制阶段的执行结果进行相应处理; HttpCoreModule的try_files指令的处理阶段,如果没有配置try_files指令,则该阶段被跳过; 该指令作用域: server ,location。 语法: try_files file1,file2,..,fileN-1 ... ,fallback 或者try_files file1,file2,..,fileN = code 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) '; } 访问: curl http://localhost/test 输出: 123 即只有content_by_lua里面的ngx.say执行了,外面的echo $b指令没有执行。echo和content_by_lua分别属于两个module的指令,他们的作用phase都是content phase。这两个module不兼容, 这两条指令不是顺序执行,至于执行结果是什么,不确定,这里是只有content_by_lua被执行了。再次证明之前的结论:作用域在同一个phase的不同modules的指令,如果没有做特殊兼容处理,则它们指令的执行顺序与指令在配置中出现的顺序无关,结果具有不确定性,需要尽量避免这种情况出现。 location /test_echo { content_by_lua ' ngx.say(ngx.var.a) '; set $a 123; set $b 456; } 日志记录阶段,该阶段记录访问日志;
参考资料: http://wiki.nginx.org/ http://wiki.nginx.org/Phases http://blog.sina.com.cn/s/blog_6d579ff40100xpff.html 以上就介紹了nginx phases 介紹,包含了方面的內容,希望對PHP教程有興趣的朋友有幫助。
|