openresty开发系列35--openresty执行流程之5内容content阶段
openresty開發系列35--openresty執行流程之5內容content階段
content 階段
---init階段---重寫賦值---重寫rewrite---access
content 階段屬于一個比較靠后的處理階段,運行在先前介紹過的 rewrite 和 access 這兩個階段之后。
當和 rewrite、access 階段的指令一起使用時,這個階段的指令總是最后運行,例如:
location /content {
??? # 重寫階段
??? set $age 1;
??? rewrite_by_lua "ngx.var.age = ngx.var.age + 1";
??? # 訪問階段
??? deny 127.0.0.1;
??? access_by_lua "ngx.var.age = ngx.var.age * 2";
??? # 內容階段
??? echo "age = $age";
}
啟動nginx? ,訪問 輸出 age = 4
改變它們的書寫順序,也不會影響到執行順序。其中,
set 指令來自 ngx_rewrite 模塊,運行于 rewrite 階段;
而 rewrite_by_lua 指令來自 ngx_lua 模塊,運行于 rewrite 階段的末尾;
接下來,deny 指令來自 ngx_access 模塊,運行于 access 階段;
再下來,access_by_lua 指令同樣來自 ngx_lua 模塊,運行于 access 階段的末尾;
最后,echo 指令則來自 ngx_echo 模塊,運行在 content 階段
----------------------------------------------
一)content_by_lua
語法:content_by_lua <lua-script-str>
默認值:無
上下文:location, location if
說明:行為類似與一個“content handler”,給每個請求執行定義于lua-script-str中的lua code。
每一個 location 只能有一個“內容處理程序”,因此,當在 location 中同時使用多個模塊的 content 階段指令時,
只有其中一個模塊能成功注冊“內容處理程序”。例如這個指令和proxy_pass指令不能同時使用在相同的location中
--------------------------------
例中的 set 指令和 rewrite_by_lua 指令同處于 rewrite 階段,
而 deny 指令和 access_by_lua 指令則同處于 access 階段。
但不幸的是echo指令,不能同時content_by_lua處于 content 階段。
考慮下面這個有問題的例子:
location /content1 {
?echo "hello ";
?content_by_lua 'ngx.say("world")';
}
訪問 http://10.11.0.215/content1 輸出 world
# 調換順序
location /content1 {
?
?content_by_lua 'ngx.say("world")';
?echo "hello ";
}
訪問 http://10.11.0.215/content1 輸出 hello
這里,ngx_echo 模塊的 echo 指令和 ngx_lua 模塊的 content_by_lua 指令同處 content 階段,
于是只有其中一個模塊能注冊和運行這個 location 的“內容處理程序”:
訪問輸出 world
輸出了后面的 content_by_lua 指令;而 echo 指令則完全沒有運行。
例中的 echo 語句和 content_by_lua 語句交換順序,則輸出就會變成 hello。
所以我們應當避免在同一個 location 中使用多個模塊的 content 階段指令。
location /content1 {
??? echo hello;
??? echo world;
}
這里使用多條 echo 指令是沒問題的,因為它們同屬 ngx_echo 模塊,而且 ngx_echo模塊規定和實現了它們之間的
執行順序。并非所有模塊的指令都支持在同一個 location 中被使用多次,例如 content_by_lua 就只能使用一次,
所以下面這個例子是錯誤的:
location /content1 {
? content_by_lua 'ngx.say("hello")';
? content_by_lua 'ngx.say("world")';
}
報錯nginx: [emerg] "content_by_lua" directive is duplicate
正確寫法:
location /content1 {
??? content_by_lua 'ngx.say("hello") ngx.say("world")';
}
----------------------------------------------
二)如果一個 location 中未使用任何 content 階段的指令,會如何處理?
靜態資源服務模塊
1) ngx_index
2) ngx_autoindex
3) ngx_static
location /content {
?? ?
}
nginx會把當前請求的 URI 映射到文件系統的靜態資源服務模塊。
當存在“內容處理程序”時,這些靜態資源服務模塊并不會起作用;反之,請求的處理權就會自動落到這些模塊上。
Nginx 一般會在 content 階段安排三個這樣的靜態資源服務模塊(除非你的 Nginx 在構造時顯式禁用了這三個模塊中
的一個或者多個,又或者啟用了這種類型的其他模塊)。按照它們在 content 階段的運行順序,依次是 ngx_index
模塊,ngx_autoindex 模塊,以及 ngx_static 模塊。
下面就來逐一介紹一下這三個模塊
?ngx_index 和 ngx_autoindex 模塊都只會作用于那些 URI 以 / 結尾的請求
?例如請求 GET /cats/,而對于不以 / 結尾的請求則會直接忽略,同時把處理權移交給 content 階段的下一個模塊。
?而 ngx_static 模塊則剛好相反,直接忽略那些 URI 以 / 結尾的請求。
以 / 結尾的請求?? ===》? ngx_index 和 ngx_autoindex 模塊 進行處理
不以 / 結尾的請求?? ===》? ngx_static 進行處理
1) ngx_index 模塊
主要用于在文件系統目錄中自動查找指定的首頁文件,類似 index.html 和 index.htm 這樣的,
?例如:
location / {
??? root?? html;
??? index? index.html index.htm;
}
當用戶請求 / 地址時,Nginx 就會自動在 root 配置指令指定的文件系統目錄下依次尋找 index.htm 和 index.html
這兩個文件。如果 index.htm 文件存在,則直接發起“內部跳轉”到 /index.htm 這個新的地址;
而如果 index.htm 文件不存在,則繼續檢查 index.html 是否存在。如果存在,同樣發起“內部跳轉”到 /index.html;
如果 index.html 文件仍然不存在,則放棄處理權給 content 階段的下一個模塊。
內部跳轉:rewrite last 內容跳轉
------------------------------------------
驗證 ngx_index 模塊在找到文件時的“內部跳轉”行為,看下面的例子
location / {
??? root?? html;
??? index? index.html;
}
location /index.html {
??? set $a 32;
??? echo "a = $a";
}
輸出 a = 32
為什么輸出不是 index.html 文件的內容?首先對于用戶的原始請求 GET /,Nginx 匹配出 location / 來處理它,
然后 content 階段的 ngx_index 模塊在 html 下找到了 index.html,于是立即發起一個到 /index.html
位置的“內部跳轉”。在重新為 /index.html 這個新位置匹配 location 配置塊時,
location /index.html 的優先級要高于 location /,因為 location 塊按照 URI 前綴來匹配時遵循所謂的
“最長子串匹配語義”。這樣,在進入 location /index.html 配置塊之后,又重新開始執行 rewrite 、access、
以及 content 等階段。最終輸出 a = 32
-------------------
如果此時把 /html/index.html 文件刪除,再訪問 / 又會發生什么事情呢?
答案是返回 403 Forbidden 出錯頁。
為什么呢?因為 ngx_index 模塊找不到 index 指令指定的文件index.html,
接著把處理權轉給 content 階段的后續模塊,而后續的模塊也都無法處理這個請求,
于是 Nginx 只好放棄,輸出了錯誤頁,并且在 Nginx 錯誤日志中留下了類似這一行信息:
??? [error] 28789#0: *1 directory index of "/html/" is forbidden
------------------
2) ngx_autoindex 模塊
所謂 directory index 便是生成“目錄索引”的意思,典型的方式就是生成一個網頁,
上面列舉出 /html/ 目錄下的所有文件和子目錄。而運行在 ngx_index 模塊之后的
ngx_autoindex 模塊就可以用于自動生成這樣的“目錄索引”網頁。我們來把上例修改一下:
??? location / {
??????? root /html/;
??????? index index.html;
??????? autoindex on;
??? }
此時仍然保持文件系統中的 /html/index.html 文件不存在。我們再訪問 / 位置時,就會得到目錄下的文件列表
---------------------
3)ngx_static 模塊
在 content 階段默認“墊底”的最后一個模塊便是極為常用的 ngx_static 模塊。
這個模塊主要實現服務靜態文件的功能。比方說,一個網站的靜態資源,包括靜態 .html 文件、靜態 .css 文件、
靜態 .js 文件、以及靜態圖片文件等等,全部可以通過這個模塊對外服務。
前面介紹的 ngx_index 模塊雖然可以在指定的首頁文件存在時發起“內部跳轉”,但真正把相應的首頁文件服務出去
(即把該文件的內容作為響應體數據輸出,并設置相應的響應頭),還是得靠這個 ngx_static 模塊來完成。
---------------------
在下面例子
location / {
??? root html;
}
在html目錄下創建hello.html文件
訪問http://10.11.0.215/hello.html
不妨來分析一下這里發生的事情:location / 中沒有使用運行在 content 階段的模塊指令,
于是也就沒有模塊注冊這個 location 的“內容處理程序”,處理權便自動落到了在 content 階段“墊底”的
那 3 個靜態資源服務模塊。
a)首先運行的 ngx_index 和 ngx_autoindex 模塊先后看到當前請求的 URI,/hello.html,并不以 / 結尾,
于是直接棄權,
b)將處理權轉給了最后運行的 ngx_static 模塊。ngx_static 模塊根據 root 指令指定的“文檔根目錄”
(document root),分別將請求 /hello.html 映射為文件系統路徑 /html/hello.html,在確認這個文件存在后,
將它們的內容分別作為響應體輸出,并自動設置 Content-Type、Content-Length 以及 Last-Modified 等響應頭。
轉載于:https://www.cnblogs.com/reblue520/p/11446470.html
總結
以上是生活随笔為你收集整理的openresty开发系列35--openresty执行流程之5内容content阶段的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: openresty开发系列34--ope
- 下一篇: openresty开发系列36--ope