天天看點

Nginx 變量漫談(一)

Nginx 的配置檔案使用的就是一門微型的程式設計語言,許多真實世界裡的 Nginx 配置檔案其實就是一個一個的小程式。當然,是不是“圖靈完全的”暫且不論,至少據我觀察,它在設計上受 Perl 和 Bourne Shell 這兩種語言的影響很大。在這一點上,相比 Apache 和 Lighttpd 等其他 Web 伺服器的配置記法,不能不說算是 Nginx 的一大特色了。既然是程式設計語言,一般也就少不了“變量”這種東西(當然,Haskell 這樣奇怪的函數式語言除外了)。

    熟悉 Perl、Bourne Shell、C/C++ 等指令式程式設計語言的朋友肯定知道,變量說白了就是存放“值”的容器。而所謂“值”,在許多程式設計語言裡,既可以是 <code>3.14</code> 這樣的數值,也可以是 <code>hello world</code> 這樣的字元串,甚至可以是像數組、哈希表這樣的複雜資料結構。然而,在 Nginx 配置中,變量隻能存放一種類型的值,因為也隻存在一種類型的值,那就是字元串。

    比如我們的 <code>nginx.conf</code> 檔案中有下面這一行配置:

    我們看到,Nginx 變量名前面有一個 <code>$</code> 符号,這是記法上的要求。所有的 Nginx 變量在 Nginx 配置檔案中引用時都須帶上 <code>$</code> 字首。這種表示方法和 Perl、PHP 這些語言是相似的。

    雖然 <code>$</code> 這樣的變量字首修飾會讓正統的 <code>Java</code> 和 <code>C#</code> 程式員不舒服,但這種表示方法的好處也是顯而易見的,那就是可以直接把變量嵌入到字元串常量中以構造出新的字元串:

這裡我們通過已有的 Nginx 變量 <code>$a</code> 的值,來構造變量 <code>$b</code> 的值,于是這兩條指令順序執行完之後,<code>$a</code> 的值是<code>hello</code>,而 <code>$b</code> 的值則是 <code>hello, hello</code>. 這種技術在 Perl 世界裡被稱為“變量插值”(variable interpolation),它讓專門的字元串拼接運算符變得不再那麼必要。我們在這裡也不妨采用此術語。

    我們來看一個比較完整的配置示例:

這個例子省略了 <code>nginx.conf</code> 配置檔案中最外圍的 <code>http</code> 配置塊以及 <code>events</code> 配置塊。使用 <code>curl</code> 這個 HTTP 用戶端在指令行上請求這個 <code>/test</code> 接口,我們可以得到

測試結果如下:

    在“變量插值”的上下文中,還有一種特殊情況,即當引用的變量名之後緊跟着變量名的構成字元時(比如後跟字母、數字以及下劃線),我們就需要使用特别的記法來消除歧義,例如:

此時 Nginx 伺服器會拒絕加載配置:

是的,我們甚至都無法啟動服務!

    有趣的是,Nginx 變量的建立和指派操作發生在全然不同的時間階段。Nginx 變量的建立隻能發生在 Nginx 配置加載的時候,或者說 Nginx 啟動的時候;而指派操作則隻會發生在請求實際處理的時候。這意味着不建立而直接使用變量會導緻啟動失敗,同時也意味着我們無法在請求處理時動态地建立新的 Nginx 變量。

    Nginx 變量一旦建立,其變量名的可見範圍就是整個 Nginx 配置,甚至可以跨越不同虛拟主機的 <code>server</code>配置塊。我們來看一個例子:

這裡我們在 <code>location /bar</code> 中用 <code>set</code> 指令建立了變量 <code>$foo</code>,于是在整個配置檔案中這個變量都是可見的,是以我們可以在 <code>location /foo</code> 中直接引用這個變量而不用擔心 Nginx 會報錯。

    下面是在指令行上用 <code>curl</code> 工具通路這兩個接口的結果:

從這個例子我們可以看到,<code>set</code> 指令因為是在 <code>location /bar</code> 中使用的,是以指派操作隻會在通路 <code>/bar</code> 的請求中執行。而請求 <code>/foo</code> 接口時,我們總是得到空的 <code>$foo</code> 值,因為使用者變量未指派就輸出的話,得到的便是空字元串。

    從這個例子我們可以窺見的另一個重要特性是,Nginx 變量名的可見範圍雖然是整個配置,但每個請求都有所有變量的獨立副本,或者說都有各變量用來存放值的容器的獨立副本,彼此互不幹擾。比如前面我們請求了<code>/bar</code> 接口後,<code>$foo</code> 變量被賦予了值 <code>32</code>,但它絲毫不會影響後續對 <code>/foo</code> 接口的請求所對應的 <code>$foo</code> 值(它仍然是空的!),因為各個請求都有自己獨立的 <code>$foo</code> 變量的副本。

    對于 Nginx 新手來說,最常見的錯誤之一,就是将 Nginx 變量了解成某種在請求之間全局共享的東西,或者說“全局變量”。而事實上,Nginx 變量的生命期是不可能跨越請求邊界的。

    (未完待續)

from:http://blog.sina.com.cn/s/blog_6d579ff40100wi7p.html