Nginx 变量漫谈(一)
Nginx 的配置文件使用的就是一门微型的编程语言,许多真实世界里的 Nginx 配置文件其实就是一个一个的小程序。当然,是不是“图灵完全的”暂且不论,至少据我观察,它在设计上受 Perl 和 Bourne Shell 这两种语言的影响很大。在这一点上,相比 Apache 和 Lighttpd 等其他 Web 服务器的配置记法,不能不说算是 Nginx 的一大特色了。既然是编程语言,一般也就少不了“变量”这种东西(当然,Haskell 这样奇怪的函数式语言除外了)。
熟悉 Perl、Bourne Shell、C/C++ 等命令式编程语言的朋友肯定知道,变量说白了就是存放“值”的容器。而所谓“值”,在许多编程语言里,既可以是 3.14
这样的数值,也可以是 hello world
这样的字符串,甚至可以是像数组、哈希表这样的复杂数据结构。然而,在 Nginx 配置中,变量只能存放一种类型的值,因为也只存在一种类型的值,那就是字符串。
比如我们的 nginx.conf
文件中有下面这一行配置:
set $a "hello world";
我们使用了标准 ngx_rewrite 模块的 set 配置指令对变量 $a
进行了赋值操作。特别地,我们把字符串 hello world
赋给了它。
我们看到,Nginx 变量名前面有一个 $
符号,这是记法上的要求。所有的 Nginx 变量在 Nginx 配置文件中引用时都须带上 $
前缀。这种表示方法和 Perl、PHP 这些语言是相似的。
虽然 $
这样的变量前缀修饰会让正统的 Java
和 C#
程序员不舒服,但这种表示方法的好处也是显而易见的,那就是可以直接把变量嵌入到字符串常量中以构造出新的字符串:
这里我们通过已有的 Nginx 变量 $a
的值,来构造变量 $b
的值,于是这两条指令顺序执行完之后,$a
的值是hello
,而 $b
的值则是 hello, hello
. 这种技术在 Perl 世界里被称为“变量插值”(variable interpolation),它让专门的字符串拼接运算符变得不再那么必要。我们在这里也不妨采用此术语。
我们来看一个比较完整的配置示例:
这个例子省略了 nginx.conf
配置文件中最外围的 http
配置块以及 events
配置块。使用 curl
这个 HTTP 客户端在命令行上请求这个 /test
接口,我们可以得到
$ curl 'http://localhost:8080/test'
foo: hello
这里我们使用第三方 ngx_echo 模块的 echo 配置指令将 $foo
变量的值作为当前请求的响应体输出。
我们看到,echo 配置指令的参数也支持“变量插值”。不过,需要说明的是,并非所有的配置指令都支持“变量插值”。事实上,指令参数是否允许“变量插值”,取决于该指令的实现模块。
如果我们想通过 echo 指令直接输出含有“美元符”($
)的字符串,那么有没有办法把特殊的 $
字符给转义掉呢?答案是否定的(至少到目前最新的 Nginx 稳定版 1.0.10
)。不过幸运的是,我们可以绕过这个限制,比如通过不支持“变量插值”的模块配置指令专门构造出取值为 $
的 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
变量赋予“美元符”这个值。
在“变量插值”的上下文中,还有一种特殊情况,即当引用的变量名之后紧跟着变量名的构成字符时(比如后跟字母、数字以及下划线),我们就需要使用特别的记法来消除歧义,例如:
这里,我们在 echo 配置指令的参数值中引用变量 $first
的时候,后面紧跟着 world
这个单词,所以如果直接写作 "$firstworld"
则 Nginx “变量插值”计算引擎会将之识别为引用了变量 $firstworld
. 为了解决这个难题,Nginx 的字符串记法支持使用花括号在 $
之后把变量名围起来,比如这里的 ${first}
. 上面这个例子的输出是:
$ curl 'http://localhost:8080/test
hello world
set 指令(以及前面提到的 geo 指令)不仅有赋值的功能,它还有创建 Nginx 变量的副作用,即当作为赋值对象的变量尚不存在时,它会自动创建该变量。比如在上面这个例子中,如果 $a
这个变量尚未创建,则 set
指令会自动创建 $a
这个用户变量。如果我们不创建就直接使用它的值,则会报错。例如
此时 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 变量的生命期是不可能跨越请求边界的。
(未完待续)
from:http://blog.sina.com.cn/s/blog_6d579ff40100wi7p.html
Nginx 变量漫谈(一)的更多相关文章
- Nginx 变量漫谈(八)
与 $arg_XXX 类似,我们在 (二) 中提到过的内建变量 $cookie_XXX 变量也会在名为 XXX 的 cookie 不存在时返回特殊值“没找到”: location /test ...
- Nginx 变量漫谈(七)
在 (一) 中我们提到过,Nginx 变量的值只有一种类型,那就是字符串,但是变量也有可能压根就不存在有意义的值.没有值的变量也有两种特殊的值:一种是“不合法”(invalid),另一种是“没找到”( ...
- Nginx 变量漫谈(五)
前面在 (二) 中我们已经了解到变量值容器的生命期是与请求绑定的,但是我当时有意避开了“请求”的正式定义.大家应当一直默认这里的“请求”都是指客户端发起的 HTTP 请求.其实在 Nginx 世界里有 ...
- Nginx 变量漫谈(四)
在设置了“取处理程序”的情况下,Nginx 变量也可以选择将其值容器用作缓存,这样在多次读取变量的时候,就只需要调用“取处理程序”计算一次.我们下面就来看一个这样的例子: map $args ...
- Nginx 变量漫谈(三)
也有一些内建变量是支持改写的,其中一个例子是 $args. 这个变量在读取时返回当前请求的 URL 参数串(即请求 URL 中问号后面的部分,如果有的话 ),而在赋值时可以直接修改参数串.我们来看一个 ...
- Nginx 变量漫谈(二)
关于 Nginx 变量的另一个常见误区是认为变量容器的生命期,是与 location 配置块绑定的.其实不然.我们来看一个涉及“内部跳转”的例子: server { listen ...
- Nginx 变量漫谈
转自:http://blog.sina.com.cn/openrestyNginx 的配置文件使用的就是一门微型的编程语言,许多真实世界里的 Nginx 配置文件其实就是一个一个的小程序.当然,是不是 ...
- Nginx 变量漫谈(六)
Nginx 内建变量用在“子请求”的上下文中时,其行为也会变得有些微妙. 前面在 (三) 中我们已经知道,许多内建变量都不是简单的“存放值的容器”,它们一般会通过注册“存取处理程序”来表现得与众不同, ...
- Nginx变量的实现机制
Nginx有两种定义变量的方式,一种是在配置文件中使用set指令(由rewrite模块提供支持),另一种是在模块内定义变量. 变量相关结构体: struct ngx_http_variable_s { ...
随机推荐
- shell之冒号的作用
冒号: :在shell中是一种命令,意思是总是为真,但是却不做任何操作,即总是为真的空命令 eg: [root@localhost ~]# ${abc:=t1}-bash: t1: comma ...
- ni
坚强歌词 马天宇 - 坚强 天使的翅膀挥动着的光芒一路走来学会了坚强每一次你努力认真的模样让我很欣赏 雨天的路上会有一缕阳光温暖被淋湿的希望再小的河也能汇成海洋让我去远航 一路上陪伴我的目光是最感动的 ...
- C语言运算符的注意问题
//对于自增和自减运算符的运算规律值得研究,小心被坑.1 #include<stdio.h> int main(void){ ,j=,p,q; p=(i++)+(i++)+(i++); q ...
- C++----练习--string 从文件中一个一个单词的读直到文件尾
从文件中读取单词.并每行显示一个: 1. #include<iostream> #include<string> #include<vector> int main ...
- windbg命令详解
DLL 该扩展仅在内核模式下使用,即使它是在Ext.dll中的. Windows NT 4.0 Ext.dll Windows 2000 Ext.dll Windows XP和之后 Ext.dll ...
- perl 爬取数据<1>
use LWP::UserAgent; use POSIX; use DBI; $user="root"; $passwd="11111111"; $dbh=& ...
- codecomb 2090【最小乘积路】
题目描述 给定n个点的带权有向图,求从1到n的路径中边权之积最小的简单路径. 输入格式 第一行读入两个整数n,m,表示共n个点m条边. 接下来m行,每行三个正整数x,y,z,表示点x到点y有一条边权为 ...
- 使用GridBagLayout控制行列的高度和宽度
摘自http://bbs.csdn.net/topics/340189065使用GridBagLayout控制行列的高度和宽度 gridwidth 指定组件显示区域的某一行中的单元格数. 默认值1,水 ...
- 【转】使用 udev 高效、动态地管理 Linux 设备文件
简介: 本文以通俗的方法阐述 udev 及相关术语的概念.udev 的配置文件和规则文件,然后以 Red Hat Enterprise Server 为平台演示一些管理设备文件和查询设备信息的实例.本 ...
- Mac 下纯lua(三)
文件处理 直接使用io调用 io.close();文件流关闭 io.flush():如果文件流以bufferd缓存模式处理,输入不会立即存入文件,需要调用本函数 io.input(file):输入 i ...