<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[静怡家园]]></title> 
<link>http://www.zhanghaijun.com/index.php</link> 
<description><![CDATA[书山有路勤为径，学海无涯苦作舟！]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[静怡家园]]></copyright>
<item>
<link>http://www.zhanghaijun.com/post//</link>
<title><![CDATA[nginx变量使用方法详解-1]]></title> 
<author>碟舞飞扬 &lt;webmaster@zhanghaijun.com&gt;</author>
<category><![CDATA[Linux技术]]></category>
<pubDate>Tue, 29 Jul 2008 07:24:18 +0000</pubDate> 
<guid>http://www.zhanghaijun.com/post//</guid> 
<description>
<![CDATA[ 
	Nginx 的配置文件使用的就是一门微型的编程语言，许多真实世界里的 Nginx 配置文件其实就是一个一个的小程序。当然，是不是“图灵完全的”暂且不论，至少据我观察，它在设计上受 Perl 和 Bourne Shell 这两种语言的影响很大。在这一点上，相比 Apache 和 Lighttpd 等其他 Web 服务器的配置记法，不能不说算是 Nginx 的一大特色了。既然是编程语言，一般也就少不了“变量”这种东西（当然，Haskell 这样奇怪的函数式语言除外了）。<br/>熟悉 Perl、Bourne Shell、C/C++ 等命令式编程语言的朋友肯定知道，变量说白了就是存放“值”的容器。而所谓“值”，在许多编程语言里，既可以是 3.14 这样的数值，也可以是 hello world 这样的字符串，甚至可以是像数组、哈希表这样的复杂数据结构。然而，在 Nginx 配置中，变量只能存放一种类型的值，因为也只存在一种类型的值，那就是字符串。<br/>比如我们的 nginx.conf 文件中有下面这一行配置：<br/><div class="code"><br/>set $a &quot;hello world&quot;;<br/></div><br/>我们使用了标准 ngx_rewrite 模块的 set 配置指令对变量 $a 进行了赋值操作。特别地，我们把字符串 hello world 赋给了它。<br/>我们看到，Nginx 变量名前面有一个 $ 符号，这是记法上的要求。所有的 Nginx 变量在 Nginx 配置文件中引用时都须带上 $ 前缀。这种表示方法和 Perl、PHP 这些语言是相似的。<br/>虽然 $ 这样的变量前缀修饰会让正统的 Java 和 C# 程序员不舒服，但这种表示方法的好处也是显而易见的，那就是可以直接把变量嵌入到字符串常量中以构造出新的字符串：<br/><div class="code"><br/>set $a hello;<br/>set $b &quot;$a, $a&quot;;<br/></div><br/>这里我们通过已有的 Nginx 变量 $a 的值，来构造变量 $b 的值，于是这两条指令顺序执行完之后，$a 的值是 hello，而 $b 的值则是 hello, hello. 这种技术在 Perl 世界里被称为“变量插值”（variable interpolation），它让专门的字符串拼接运算符变得不再那么必要。我们在这里也不妨采用此术语。<br/>我们来看一个比较完整的配置示例：<br/><div class="code"><br/>server &#123;<br/>listen 8080;<br/>location /test &#123;<br/>set $foo hello;<br/>echo &quot;foo: $foo&quot;;<br/>&#125;<br/>&#125;<br/></div><br/>这个例子省略了 nginx.conf 配置文件中最外围的 http 配置块以及 events 配置块。使用 curl 这个 HTTP 客户端在命令行上请求这个 /test 接口，我们可以得到<br/><div class="code"><br/>$ curl &#039;http://localhost:8080/test&#039;<br/>foo: hello<br/></div><br/>这里我们使用第三方 ngx_echo 模块的 echo 配置指令将 $foo 变量的值作为当前请求的响应体输出。<br/>我们看到，echo 配置指令的参数也支持“变量插值”。不过，需要说明的是，并非所有的配置指令都支持“变量插值”。事实上，指令参数是否允许“变量插值”，取决于该指令的实现模块。<br/>如果我们想通过 echo 指令直接输出含有“美元符”（$）的字符串，那么有没有办法把特殊的 $ 字符给转义掉呢？答案是否定的（至少到目前最新的 Nginx 稳定版 1.0.10）。不过幸运的是，我们可以绕过这个限制，比如通过不支持“变量插值”的模块配置指令专门构造出取值为 $ 的 Nginx 变量，然后再在 echo 中使用这个变量。看下面这个例子：<br/><div class="code"><br/>geo $dollar &#123;<br/>default &quot;$&quot;;<br/>&#125;<br/>server &#123;<br/>listen 8080;<br/>location /test &#123;<br/>echo &quot;This is a dollar sign: $dollar&quot;;<br/>&#125;<br/>&#125;<br/></div><br/>测试结果如下：<br/><div class="code"><br/>$ curl &#039;http://localhost:8080/test&#039;<br/>This is a dollar sign: $<br/></div><br/>这里用到了标准模块 ngx_geo 提供的配置指令 geo 来为变量 $dollar 赋予字符串 &quot;$&quot;，这样我们在下面需要使用美元符的地方，就直接引用我们的 $dollar 变量就可以了。其实 ngx_geo 模块最常规的用法是根据客户端的 IP 地址对指定的 Nginx 变量进行赋值，这里只是借用它以便“无条件地”对我们的 $dollar 变量赋予“美元符”这个值。<br/>在“变量插值”的上下文中，还有一种特殊情况，即当引用的变量名之后紧跟着变量名的构成字符时（比如后跟字母、数字以及下划线），我们就需要使用特别的记法来消除歧义，例如：<br/><div class="code"><br/>server &#123;<br/>listen 8080;<br/>location /test &#123;<br/>set $first &quot;hello &quot;;<br/>echo &quot;$&#123;first&#125;world&quot;;<br/>&#125;<br/>&#125;<br/></div><br/>这里，我们在 echo 配置指令的参数值中引用变量 $first 的时候，后面紧跟着 world 这个单词，所以如果直接写作 &quot;$firstworld&quot; 则 Nginx “变量插值”计算引擎会将之识别为引用了变量 $firstworld. 为了解决这个难题，Nginx 的字符串记法支持使用花括号在 $ 之后把变量名围起来，比如这里的 $&#123;first&#125;. 上面这个例子的输出是：<br/><div class="code"><br/>$ curl &#039;http://localhost:8080/test<br/>hello world<br/></div><br/>set 指令（以及前面提到的 geo 指令）不仅有赋值的功能，它还有创建 Nginx 变量的副作用，即当作为赋值对象的变量尚不存在时，它会自动创建该变量。比如在上面这个例子中，如果 $a 这个变量尚未创建，则 set 指令会自动创建 $a 这个用户变量。如果我们不创建就直接使用它的值，则会报错。例如<br/><div class="code"><br/>server &#123;<br/>listen 8080;<br/>location /bad &#123;<br/>echo $foo;<br/>&#125;<br/>&#125;<br/></div><br/>此时 Nginx 服务器会拒绝加载配置:<br/><div class="code"><br/>&#91;emerg&#93; unknown &quot;foo&quot; variable<br/></div><br/>是的，我们甚至都无法启动服务！<br/>有趣的是，Nginx 变量的创建和赋值操作发生在全然不同的时间阶段。Nginx 变量的创建只能发生在 Nginx 配置加载的时候，或者说 Nginx 启动的时候；而赋值操作则只会发生在请求实际处理的时候。这意味着不创建而直接使用变量会导致启动失败，同时也意味着我们无法在请求处理时动态地创建新的 Nginx 变量。<br/>Nginx 变量一旦创建，其变量名的可见范围就是整个 Nginx 配置，甚至可以跨越不同虚拟主机的 server 配置块。我们来看一个例子：<br/><div class="code"><br/>server &#123;<br/>listen 8080;<br/>location /foo &#123;<br/>echo &quot;foo = &#91;$foo&#93;&quot;;<br/>&#125;<br/>location /bar &#123;<br/>set $foo 32;<br/>echo &quot;foo = &#91;$foo&#93;&quot;;<br/>&#125;<br/>&#125;<br/></div><br/>这里我们在 location /bar 中用 set 指令创建了变量 $foo，于是在整个配置文件中这个变量都是可见的，因此我们可以在 location /foo 中直接引用这个变量而不用担心 Nginx 会报错。<br/>下面是在命令行上用 curl 工具访问这两个接口的结果：<br/><div class="code"><br/>$ curl &#039;http://localhost:8080/foo&#039;<br/>foo = &#91;&#93;<br/> <br/>$ curl &#039;http://localhost:8080/bar&#039;<br/>foo = &#91;32&#93;<br/> <br/>$ curl &#039;http://localhost:8080/foo&#039;<br/>foo = &#91;&#93;<br/></div><br/>从这个例子我们可以看到，set 指令因为是在 location /bar 中使用的，所以赋值操作只会在访问 /bar 的请求中执行。而请求 /foo 接口时，我们总是得到空的 $foo 值，因为用户变量未赋值就输出的话，得到的便是空字符串。<br/>从这个例子我们可以窥见的另一个重要特性是，Nginx 变量名的可见范围虽然是整个配置，但每个请求都有所有变量的独立副本，或者说都有各变量用来存放值的容器的独立副本，彼此互不干扰。比如前面我们请求了 /bar 接口后，$foo 变量被赋予了值 32，但它丝毫不会影响后续对 /foo 接口的请求所对应的 $foo 值（它仍然是空的！），因为各个请求都有自己独立的 $foo 变量的副本。<br/>对于 Nginx 新手来说，最常见的错误之一，就是将 Nginx 变量理解成某种在请求之间全局共享的东西，或者说“全局变量”。而事实上，Nginx 变量的生命期是不可能跨越请求边界的。<br/>Tags - <a href="http://www.zhanghaijun.com/tags/nginx/" rel="tag">nginx</a>
]]>
</description>
</item><item>
<link>http://www.zhanghaijun.com/post//#blogcomment</link>
<title><![CDATA[[评论] nginx变量使用方法详解-1]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.zhanghaijun.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>