首頁  >  文章  >  後端開發  >  nginx phases 介紹

nginx phases 介紹

WBOY
WBOY原創
2016-08-08 09:27:311189瀏覽
一、nginx的11個phases

一個請求經過nginx處理的過程中,會經過一系列的階段(phases),下面這個表格列出了nginx的所有phases,每個階段可選的退出方式,包含的模組和對應的指令

配置查找階段NGX_HTTP_REWRITE_PHASE Location(location rewrite)location 。重寫提交階段NGX_HTTP_PREACCESS_PHASE degradation, NginxHttpLimitZoneModule / limit_zone, 存取權限檢查準備階段存取權檢查階段NGX_HTTP_POST_ACCESS_PHASE  存取權限檢查提交階段NGX_HTTP_TRY_FILES_PHASElocation selectionHttpNGCoreModule / 處理階段_CONTENT_PHASE HttpEmptyGifModule / EmptyGif, HttpFcgiModule / FastCGI, HttpFlvStreamModul / FLV, HttpProxyModule / proxy, HttpProxyModule / random_index,HttpScgiModule / scgi, HttpStubStatusModule / stub_status,  HttpUwsgiModule / uwsgi HttpLuaModule / content_by_lua,日誌模組處理階段
phase optional exits modules / pRealIpModule 讀取請求內容階段
NGX_HTTP_SERVER_REWRITE_PHASE (server rewrite)   HttpRewriteModule / rewrite 請求地址重寫階段
NGX_HTTP_FIND_CONFIG_PHASE(lo
HttpLimitReqModule / limit req, HttpRealIpModule
NGX_HTTP_c/c面AuthBasicModule / auth_basic

HttpAutoindexModule / autoindex,HttpCoreModule / Core, HttpDavModule / DAV,
HttpGzipStaticModule / gzip_static, HttpIndem / perl,
HttpCoreModule / proxy_pass 內容產生階段 NGX_HTTP_LOGP_HThTPSm&Mr8m ​​al
二、各個phase說明:
post read phase:

讀完請求頭後就進入了post_read 階段,它位於uri被重寫之前,這個階段允許nginx改變請求頭中就進入了post_read 階段,它位於uri被重寫之前,這個階段允許nginx改變請求頭中的值,相關模組HttpRealIpModule.

server_rewrite phase:

這個階段主要進行初始化全域變量,或是server層級的重寫。如果把重寫指令放到 server 中,那就進入了server rewrite 階段。 (重寫指令見rewrite phase)

find config phase:

這個階段使用重寫之後的uri來找出對應的location,值得注意的是該階段可能會被執行多次,因為也可能會被執行多次有location層級的重寫指令。

rewrite phase:

如果把重寫指令放到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指令。
结论:即使运行在同一个请求处理阶段,分属不同模块的配置指令也可能会分开独立运行(除非像 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;
     }

访问: curl 'http://localhost/test'
输出: 57

说明: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的指令,如果没有做特殊兼容处理,则它们指令的执行顺序与指令在配置中出现的顺序无关,结果具有不确定性

post rewrite phase:

location级别重写的下一阶段,用来检查上阶段是否有uri重写,并根据结果跳转到合适的阶段;

preaccess phase:

访问权限控制的前一阶段,该阶段在权限控制阶段之前,一般也用于访问控制,比如限制访问频率,链接数等;相关模块/指令 :NginxHttpLimitZoneModule / limit_zone, 
HttpLimitReqModule / limit req, HttpRealIpModule

access phase:

访问权限控制阶段,比如基于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";
    }

本机访问: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错误。
原因说明:同属于HttpAccessModule这个模块的多条配置指令之间是按顺序执行的,直到遇到第一条满足条件的指令就不再执行后续的 allow 和 deny 指令。如果首先匹配的指令是 allow,则会继续执行后续其他模块的指令或者跳到后续的处理阶段;而如果首先满足的是 deny 则会立即中止当前整个请求的处理,并立即返回给客户端 403 错误页。

结论:同一个phase的同一个module内的多条指令的执行顺序由这个module自己来定义。

因为 HttpAccessModule的指令运行在 access 阶段,而 access 阶段又处于 rewrite 阶段之后,所以前面我们见到的所有那些在 rewrite 阶段运行的配置指令,都总是在 allow 和 deny 之前执行,而无论它们在配置文件中的书写顺序是怎样的。所以,为了避免阅读配置时的混乱,我们应该总是让指令的书写顺序和它们的实际执行顺序保持一致。

post access phase:

访问权限控制的后一阶段,该阶段根据权限控制阶段的执行结果进行相应处理;

try files phase:

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
content phase:

内容生成阶段,该阶段产生响应,并发送到客户端; 这个阶段相关的模块和指令较多。这里仅拿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;
    }
log phase:

日志记录阶段,该阶段记录访问日志;

三、小结:
  • 11个phases粗略介绍完了。这11个phases,不是每个请求都会经历所有的11个phase。有可能某些phase没有经历,如果请求内部包含子请求,某些phase可能会出现多次。
  • 在单个请求的处理过程中,前面的phase总是在后面phase之前执行。这里的前后值得是本文phase的介绍顺序,而不是phase相关的指令在配置文件中出现的顺序。
    例如,rewrite phase总是在content phase之前执行,与指令在配置文件中出现顺序无关,下面的例子
    location /test {
        set $a 11;
        echo $a; 
        set $a 22;
        echo $a;
    }
    

    实际执行顺序为set $a 11;set $a 22;echo $a;echo $a;
    访问:curl http://localhost/test
    输出:
    22 
    22
    理由是rewrite phase总是在content phase之前执行
  • 作用域为同一个phase的不同modules的指令,如果modules之间做了特殊的兼容,则它们按照指令在配置文件中出现的顺序依次执行下来
  • 作用域在同一个phase的不同modules的指令,如果没有做特殊兼容处理,则它们指令的执行顺序与指令在配置中出现的顺序无关,结果具有不确定性
  • 同一个phase的同一个module内的多条指令的执行顺序由这个module自己来定义。

参考资料:

http://wiki.nginx.org/

http://wiki.nginx.org/Phases

http://blog.sina.com.cn/s/blog_6d579ff40100xpff.html

以上就介紹了nginx phases 介紹,包含了方面的內容,希望對PHP教程有興趣的朋友有幫助。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn