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 等響應頭。css