1. 预备知识

Nginx有两种定义变量的方法,一种是在模块中定义,从而成为内建变量;另一种是在配置文件中通过set指令来定义。

配置过Nginx的童鞋可能都知道这样一个事实:一个请求在其处理过程中,即使经历多个不同的 location 配置块,它使用的还是同一套 Nginx 变量的副本。

例如,如果有如下的配置:

location /test1 {  
        set $hello hello;  
        rewrite /test1 /test2 last;

}
  
location /test2 {  
        echo $hello;  
}  

虽然变量是定义在location /test1中,但Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,所以在/test2中直接使用该变量是不会报错的。另一方面,由于变量的生命周期是与请求相关的,所以如果直接访问/test2,得到的变量$hello是一个空值,而访问/test1,则会内部跳转到/test2,将/test1中赋值的hello打印出来。

测试如下所示:
nginx-1.png

2. 如果变量定义在server处

例如,如果有如下的配置:

set $hello hello;  
location /test1 {  
        echo $hello;  
}  
  
location /test2 {  
        echo $hello;  
}  

那么很容易想到,无论是访问/test1还是/test2,都会输出在上层定义的变量$hello的值,测试如下:
nginx-2.png

再进一步,假设配置如下:

set $hello hello;  
location /test1 {  
        set $hello bye;  
        rewrite /test1 /test2 last;  
}  
  
location /test2 {  
        echo $hello;  
}  

则同样很容易想到,如果直接访问/test1,则会先把在server段定义的$hello变量由“hello”改写为”bye”,再进行内部跳转到/test2后,则会输出新的修改过后的变量$hello;而如果直接访问/test2,则会输出在server段定义的变量。测试如下:
nginx-3.png

3. 坑来了

到现在为止,一切都很正常,上述的知识点也是Nginx配置中的基础知识。但当和ngx_lua模块结合起来时,就时常会碰到比较诡异的事情。
假设配置如下:

set $hello hello;  
  
location /test1 {  
        content_by_lua '  
                ngx.var.hello = "bye";  
                ngx.exec("/test2");  
        ';  
}  
  
location /test2 {  
        echo "in test2";  
        echo $hello;  
}  

我们来猜猜分别访问/test1和/test2,会输出什么。首先,server段已经定义了变量$hello为字符串”hello”,直接访问/test1时,会将其修改为”bye”,然后执行ngx.exec跳转到/test2,输出变量$hello。但经过测试,分别访问/test1和/test2的时候,结果如下所示:
nginx-4.png
是不是很奇怪?直接访问/test2输出hello是正常的,可是访问/test1居然也是输出hello,在/test1中对hello变量的更改似乎没起作用。

到底/test1的content_by_lua段中,对变量$test的修改起作用没,我们可以测试一下,添加一行调试代码如下:

set $hello hello;  
  
location /test1 {  
        content_by_lua '  
                ngx.var.hello = "bye";  
                ngx.log(ngx.ERR, ngx.var.hello);  
                ngx.exec("/test2");  
        ';  
}  
  
location /test2 {  
        echo "in test2";  
        echo $hello;  
}  

再次访问/test1,我们会发现,在log中记录下来了此时的$hello变量为”bye”。可是为什么执行内部跳转到/test2后,输出的结果还是”hello”呢?

我们再次改造一下配置:

location /test1 {  
        set $hello hello;  
        content_by_lua '  
                ngx.var.hello = "bye";  
                ngx.log(ngx.ERR, ngx.var.hello);  
                ngx.exec("/test2");  
        ';  
}  
  
location /test2 {  
        echo "in test2";  
        echo $hello;  
}  

和上面的区别在于,这次将变量的定义从server段迁到了location /test1中,在/test1中的content_by_lua段对其进行修改,再执行内部跳转到/test2。测试结果如下:
nginx-5.png
这次的结果显然是符合我们的预期的。直接访问/test1,由于在content_by_lua中将$hello变量更改为”bye”了,所以当内部跳转到/test2时,输出$hello变量就会输出“bye”;如果直接访问/test2,则由于此时并没有对$hello变量赋值,所以$hello变量为空。