在設定了「取處理程式」的情況下,nginx 變數也可以選擇將其值容器用作快取,這樣在多次讀取變數的時候,就只需要呼叫「取處理程式」計算一次。我們下面就來看乙個這樣的例子:
map $args $fooserver
}
這裡首次用到了標準 ngx_map 模組的 map 配置指令,我們有必要在此介紹一下。map
在英文中除了「地圖」之外,也有「對映」的意思。比方說,中學數學裡講的「函式」就是一種「對映」。而 nginx 的這個 map 指令就可以用於定義兩個 nginx 變數之間的對映關係,或者說是函式關係。回到上面這個例子,我們用 map 指令定義了使用者變數$foo
與 $args 內建變數之間的對映關係。特別地,用數學上的函式記法y = f(x)
來說,我們的$args
就是「自變數」x
,而$foo
則是「因變數」y
,即$foo
的值是由 $args 的值來決定的,或者按照書寫順序可以說,我們將 $args 變數的值對映到了$foo
變數上。
現在我們再來看 map 指令定義的對映規則:
map $args $foo
花括號中第一行的default
是乙個特殊的匹配條件,即當其他條件都不匹配的時候,這個條件才匹配。當這個預設條件匹配時,就把「因變數」$foo
對映到值0
. 而花括號中第二行的意思是說,如果「自變數」$args
精確匹配了debug
這個字串,則把「因變數」$foo
對映到值1
. 將這兩行合起來,我們就得到如下完整的對映規則:當 $args 的值等於debug
的時候,$foo
變數的值就是1
,否則$foo
的值就為0
.
明白了 map 指令的含義,再來看location /test
. 在那裡,我們先把當前$foo
變數的值儲存在另乙個使用者變數$orig_foo
中,然後再強行把 $args 的值改寫為debug
,最後我們再用 echo 指令分別輸出$orig_foo
和$foo
的值。
從邏輯上看,似乎當我們強行改寫 $args 的值為debug
之後,根據先前的 map 對映規則,$foo
變數此時的值應當自動調整為字串1
, 而不論$foo
原先的值是怎樣的。然而測試結果並非如此:
第一行輸出指示$orig_foo
的值為0
,這正是我們期望的:上面這個請求並沒有提供 url 引數串,於是 $args最初的取值就是空,再根據我們先前定義的對映規則,$foo
變數在第一次被讀取時的值就應當是0
(即匹配預設的那個default
條件)。
而第二行輸出顯示,在強行改寫 $args 變數的值為字串debug
之後,$foo
的條件仍然是0
,這顯然不符合對映規則,因為當 $args 為debug
時,$foo
的值應當是1
. 這究竟是為什麼呢?
其實原因很簡單,那就是$foo
變數在第一次讀取時,根據對映規則計算出的值被快取住了。剛才我們說過,nginx 模組可以為其建立的變數選擇使用值容器,作為其「取處理程式」計算結果的快取。顯然,ngx_map模組認為變數間的對映計算足夠昂貴,需要自動將因變數的計算結果快取下來,這樣在當前請求的處理過程中如果再次讀取這個因變數,nginx 就可以直接返回快取住的結果,而不再呼叫該變數的「取處理程式」再行計算了。
為了進一步驗證這一點,我們不妨在請求中直接指定 url 引數串為debug
:
我們看到,現在$orig_foo
的值就成了1
,因為變數$foo
在第一次被讀取時,自變數 $args 的值就是debug
,於是按照對映規則,「取處理程式」計算返回的值便是1
. 而後續再讀取$foo
的值時,就總是得到被快取住的1
這個結果,而不論 $args 後來變成什麼樣了。
map 指令其實是乙個比較特殊的例子,因為它可以為使用者變數註冊「取處理程式」,而且使用者可以自己定義這個「取處理程式」的計算規則。當然,此規則在這裡被限定為與另乙個變數的對映關係。同時,也並非所有使用了「取處理程式」的變數都會快取結果,例如我們前面在 (三) 中已經看到 $arg_*** 並不會使用值容器進行快取。
類似 ngx_map 模組,標準的 ngx_geo 等模組也一樣使用了變數值的快取機制。
在上面的例子中,我們還應當注意到 map 指令是在server
配置塊之外,也就是在最外圍的http
配置塊中定義的。很多讀者可能會對此感到奇怪,畢竟我們只是在location /test
中用到了它。這倒不是因為我們不想把map
語句直接挪到location
配置塊中,而是因為 map 指令只能在http
塊中使用!
很多 nginx 新手都會擔心如此「全域性」範圍的 map 設定會讓訪問所有虛擬主機的所有location
介面的請求都執行一遍變數值的對映計算,然而事實並非如此。前面我們已經了解到 map 配置指令的工作原理是為使用者變數註冊 「取處理程式」,並且實際的對映計算是在「取處理程式」中完成的,而「取處理程式」只有在該使用者變數被實際讀取時才會執行(當然,因為快取的存在,只在請求生命期中的第一次讀取中才被執行),所以對於那些根本沒有用到相關變數的請求來說,就根本不會執行任何的無用計算。
這種只在實際使用物件時才計算物件值的技術,在計算領域被稱為「惰性求值」(lazy evaluation)。提供「惰性求值」 語義的程式語言並不多見,最經典的例子便是 haskell. 與之相對的便是「主動求值」 (eager evaluation)。我們有幸在 nginx 中也看到了「惰性求值」的例子,但「主動求值」語義其實在 nginx 裡面更為常見,例如下面這行再普通不過的 set 語句:
set $b "$a,$a";
這裡會在執行 set 規定的賦值操作時,「主動」地計算出變數$b
的值,而不會將該求值計算延緩到變數$b
實際被讀取的時候。
(未完待續)
Nginx 變數漫談(三)
太棒了 變數漫談 三 rel noopener noreferrer nginx 變數漫談 三 agentzh 也有一些內建變數是支援改寫的,其中乙個例子是 args.這個變數在讀取時返回當前請求的 url 引數串 即請求 url 中問號後面的部分,如果有的話 而在賦值時可以直接修改引數串。我們來看...
Nginx 變數漫談(六)
nginx 內建變數用在 子請求 的上下文中時,其行為也會變得有些微妙。前面在 三 中我們已經知道,許多內建變數都不是簡單的 存放值的容器 它們一般會通過註冊 訪問處理程式 來表現得與眾不同,而它們即使有存放值的容器,也只是用於快取 訪問處理程式 的計算結果。我們之前討論過的 args 變數正是通過...
Nginx 變數漫談(八)
與 arg 類似,我們在 二 中提到過的內建變數 cookie 變數也會在名為 的 cookie 不存在時返回特殊值 沒找到 location test 利用curl命令列工具的 cookie name value選項可以指定name value為當前請求攜帶的 cookie 通過新增相應的cook...