Nginx 的配置文件使用的就是一门微型的编程语言,许多真实世界里的 Nginx 配置文件其实就是一个一个的小程序。固然,是否是“图灵彻底的”暂且不论,它在设计上受 Perl 和 Bourne Shell 这两种语言的影响很大。在这一点上,相比 Apache 和 Lighttpd 等其余 Web 服务器的配置记法,不能不说算是 Nginx 的一大特点了。既然是编程语言,通常也就少不了“变量”这种东西(固然,Haskell 这样奇怪的函数式语言除外了)。html
熟悉 Perl、Bourne Shell、C/C++ 等命令式编程语言的朋友确定知道,变量说白了就是存放“值”的容器。而所谓“值”,在许多编程语言里,既能够是 3.14
这样的数值,也能够是 hello world
这样的字符串,甚至能够是像数组、哈希表这样的复杂数据结构。然而,在 Nginx 配置中,变量只能存放一种类型的值,由于也只存在一种类型的值,那就是字符串。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),它让专门的字符串拼接运算符变得再也不那么必要。咱们在这里也不妨采用此术语。数据结构
咱们来看一个比较完整的配置示例:curl
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 配置指令的参数也支持“变量插值”。不过,须要说明的是,并不是全部的配置指令都支持“变量插值”。事实上,指令参数是否容许“变量插值”,取决于该指令的实现模块。
若是咱们想经过 echo 指令直接输出含有“美圆符”($)的字符串,那么有没有办法把特殊的$字符给转义掉呢?答案是否认的。不过幸运的是,咱们能够绕过这个限制,好比经过不支持“变量插值”的模块配置指令专门构造出取值为$ 的 Nginx 变量,而后再在 echo 中使用这个变量。看下面这个例子:
geo $dollar { default "$"; } server { listen 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 服务器会拒绝加载配置:
[emerg] unknown "foo" variable
是的,咱们甚至都没法启动服务!
有趣的是,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]
$ curl 'http://localhost:8080/foo'
foo = []
从这个例子咱们能够看到,set
指令由于是在 location /bar
中使用的,因此赋值操做只会在访问 /bar
的请求中执行。而请求 /foo
接口时,咱们老是获得空的 $foo
值,由于用户变量未赋值就输出的话,获得的即是空字符串。
从这个例子咱们能够窥见的另外一个重要特性是,Nginx 变量名的可见范围虽然是整个配置,但每一个请求都有全部变量的独立副本,或者说都有各变量用来存放值的容器的独立副本,彼此互不干扰。好比前面咱们请求了/bar
接口后,$foo
变量被赋予了值 32
,但它丝绝不会影响后续对 /foo
接口的请求所对应的 $foo
值(它仍然是空的!),由于各个请求都有本身独立的 $foo
变量的副本。
对于 Nginx 新手来讲,最多见的错误之一,就是将 Nginx 变量理解成某种在请求之间全局共享的东西,或者说“全局变量”。而事实上,Nginx 变量的生命期是不可能跨越请求边界的。