nginx变量(1)转载自章亦春

nginx配置文件实际上就是一个个的小程序,nginx的配置文件使用的就是一门微型的编程语言,既然是编程语言,也就少不了“变量”。说白了,变量就是存放“值”的容器。所谓“值”,在许多的编程语句里面,既可以是3.14这样的数值,也可以是hello,worid这样的字符串,甚至可以是像数组、哈希表这样的复杂数据结构。然而,在nginx配置中,变量只能存放一种类型的值,因为也只有存在这样一种类型的值,那就是字符串。

比如我们的nginx.conf文件中有下面一行配置:

set $a "hello world";

我们使用了标准的ngx_rewrite模块的set配置指令对变量$a进行了赋值操作。特别地,我们把字符串hello world赋给了它。我们看到了,nginx变量名前面有一个$符号,这个是记法的要求。所有的nginx变量在nginx配置文件中引用都必须带上$前缀。这种表示方法和perl、php这些语言是相识的。 

虽然$这样的变量前缀会让正统的java和c#程序员感到不舒服,但是这样的表示方法的好处也是显而易见的,那就是可以直接把变量嵌入刀字符串常量中以构造新的字符串:

set $a hello;

set $b "$a,$a";

这里我们通过已有的nginx变量$a的值,来构造变量$b的值,于是这两条指令顺序执行完之后,$a的值是hello,而$b的值是hello,hello。这种技术在perl世界里被称为”变量插值“(variable interpolation),它让专门的字符串拼接运算符变得不再那么必要。

实例

server{

listen 8080:

location /test{

set $foo hello;

echo "foo: $foo";

            }

}

这个例子省略了nginx.conf配置文件中最外围的http配置以及events配置块。使用curl这个http客户端在命令行上请求这个/test接口,我们可以得到

$curl ‘http://localhost:8080/test'

foo: hello

这个我们使用第三方ngx_echo模块的echo配置指令将$foo变量的值作为当前请求的响应体输出。

我们看到,echo配置指令的参数也支持”变量插值“。不过,需要说明的是,并非所有的配置指令都支持”变量插值“。事实上,指令参数是否允许”变量插值“,取决于该指令的实现那模块。

如果我们想通过echo指令直接输出含有($)的字符串,那么是不可能的。不过幸运的是,我们可以绕过这个限制,比如通过不支持”变量插值“的模块配置指令专门构造出取值为$的nginx变量,然后再在echo中使用这个变量。看下面的例子:

geo $dollar{

default "$";

}

server {

listen 8080;

location 8080;

location /test {

echo "this is a dollar sign:$dollar";

}

}

测试结果如下:

$ curl 'http://localhost:8080/test'

this is a dollar sign : $

这里用到了标准模块ngx_geo提供的配置指令geo来为变量$dollar赋予字符串”$",这样我们在下面需要是使用$符号的地方,就直接引用我们的$dollar变量就行了。其实ngx_geo模块最常规的用法是根据客户端的ip地址对指定的nginx变量进行赋值。这里只是借用了它用来“无条件地”对我们的$dollar变量赋予“美元符号”这个值。

在“变量插值”的上下文中,还有一种特殊情况,即当引用的变量名之后紧跟着变量名的构成字符时(比如后跟字母、数字、下划线),我们就需要使用特别的记法来消除歧义,例如:

server{

listen 8080;

location /test{

set $first "hello";

echo "${first}world";

}

}

这里,我们在echo配置指令的参数值中引用变量$first的时候,后面紧跟着world这个单词,所以如果直接写作$firstworld则nginx“变量插值计算引擎将会识别变量$firstworld.为了解决这个难题,nginx的字符串记法支持使用花括号在$之后把变量名围起来,比如这里的${first}.上面的例子输出就是:

 $curl 'http://localhost:8080/test

hello world

set指令(以及前面提到的geo指令)不仅有赋值的功能,它还有创建nginx变量的副作用,即当作为赋值对象的变量尚不存在,它会自动创建该变量。比如在上面这个列子中,如果$a这个变量尚未创建,则set指令会自动创建$a这个用户变量。如果我们不创建就直接使用它的值,则会报错。例如

server{

listen 8080;

location /bad {

echo $foo;

}

}

此时,nginx服务器就会拒绝加载配置:

【emerged】unkown "foo" variable

nginx变量的创建和赋值操作发生在全然不同的时间阶段。nginx变量的创建只能发生在nginx配置加载的时候,或者说nginx启动的时候;而赋值操作则只会发生在请求实际处理的时候。这意味着不创建而直接使用变量会导致nginx启动失败,同时也意味着我们无法在请求动态地创建新的nginx变量。

nginx变量一旦创建,其变量名的可见范围就是整个nginx配置,甚至可以跨越不同的虚拟主机的server配置块。我们来看一个例子:

server{

listen 8080;

location /foo{

echo "foo = [$foo]";

}

location /bar {

set $foo 32;

echo "foo = [$foo];

}

}

这里我们在location /bar中用set指令创建了变量$foo,于是在整个配置文件中这个变量都是可见的,因此我们可以在location /foo中直接引用这个变量而不用担心nginx会报错。

下面在命令行上用curl工具来访问这两个街斗的结果:

$curl 'http://localhost:8080/foo'

foo = []

$curl 'http://localhost:8080/bar'

foo = [32]

$furl 'http://localhost:8080/foo'

foo = []

这个例子我们可以看到,set指令因为在location /bar中使用的,所以赋值操作只会在访问/bar的请求中执行。而请求/foo接口的时候,我们总是得到空的$foo值,因为用户在未赋值就输出的话,得到的便是空字符串。

这个例子我们可以窥见的另一个重要特性是,nginx变量名的可见范围虽然是整个配置,但是每个请求都有多有变量的独立副本,或者说各变量用来存放值的容器的独立副本,但彼此互不干扰。比如前面我们请求了/bar接口后,$foo变量被赋予了值32,但它丝毫不会影响到后续对/foo接口对应的$foo 值(它仍然是空),因为各个请求都有自己独立的$foo变量的副本。

对于nignx新手来说,最常见的错误之一,就是将nignx变量理解成某种在请求之间全局共享的东西,或者说"全局变量”。而事实上,nginx变量的生命周期是不可能跨越请求边界的。

没有更多推荐了,返回首页