【转帖】nginx变量使用方法详解-5

nginx,变量,使用,方法,详解 · 浏览次数 : 0

小编点评

**Nginx变量值容器的生命期是与当前请求相关联的。** 每个请求都有所有变量值容器的独立副本,但这些变量值容器只在当前请求中有效。当请求发起一个子请求时,其变量值容器会被共享给该子请求,而“父请求”的变量值容器不会被影响。 **子请求与“父请求”共享变量值的两种方式:** * **共享父请求中的变量值容器:**第三方模块如 ngx_auth_request 和 ngx_echo 在使用变量共享时会默认使用父请求中的变量值容器。 * **使用嵌套配置来实现变量共享:**例如,使用 ngx_auth_request 模块可以嵌套配置以实现子请求与父请求之间的变量共享。

正文

https://www.diewufeiyang.com/post/579.html

 

前面在 (二) 中我们已经了解到变量值容器的生命期是与请求绑定的,但是我当时有意避开了“请求”的正式定义。大家应当一直默认这里的“请求”都是指客户端发起的 HTTP 请求。其实在 Nginx 世界里有两种类型的“请求”,一种叫做“主请求”(main request),而另一种则叫做“子请求”(subrequest)。我们先来介绍一下它们。

所谓“主请求”,就是由 HTTP 客户端从 Nginx 外部发起的请求。我们前面见到的所有例子都只涉及到“主请求”,包括 (二) 中那两个使用 echo_exec 和 rewrite 指令发起“内部跳转”的例子。

而“子请求”则是由 Nginx 正在处理的请求在 Nginx 内部发起的一种级联请求。“子请求”在外观上很像 HTTP 请求,但实现上却和 HTTP 协议乃至网络通信一点儿关系都没有。它是 Nginx 内部的一种抽象调用,目的是为了方便用户把“主请求”的任务分解为多个较小粒度的“内部请求”,并发或串行地访问多个 location 接口,然后由这些 location 接口通力协作,共同完成整个“主请求”。当然,“子请求”的概念是相对的,任何一个“子请求”也可以再发起更多的“子子请求”,甚至可以玩递归调用(即自己调用自己)。当一个请求发起一个“子请求”的时候,按照 Nginx 的术语,习惯把前者称为后者的“父请求”(parent request)。值得一提的是,Apache 服务器中其实也有“子请求”的概念,所以来自 Apache 世界的读者对此应当不会感到陌生。

下面就来看一个使用了“子请求”的例子:

Bash
    location /main {
        echo_location /foo;
        echo_location /bar;
    }
 
    location /foo {
        echo foo;
    }
 
    location /bar {
        echo bar;
    }

这里在 location /main 中,通过第三方 ngx_echo 模块的 echo_location 指令分别发起到 /foo 和 /bar 这两个接口的 GET 类型的“子请求”。由 echo_location 发起的“子请求”,其执行是按照配置书写的顺序串行处理的,即只有当 /foo 请求处理完毕之后,才会接着处理 /bar 请求。这两个“子请求”的输出会按执行顺序拼接起来,作为 /main 接口的最终输出:

Bash
    $ curl 'http://localhost:8080/main'
    foo
    bar

我们看到,“子请求”方式的通信是在同一个虚拟主机内部进行的,所以 Nginx 核心在实现“子请求”的时候,就只调用了若干个 C 函数,完全不涉及任何网络或者 UNIX 套接字(socket)通信。我们由此可以看出“子请求”的执行效率是极高的。

回到先前对 Nginx 变量值容器的生命期的讨论,我们现在依旧可以说,它们的生命期是与当前请求相关联的。每个请求都有所有变量值容器的独立副本,只不过当前请求既可以是“主请求”,也可以是“子请求”。即便是父子请求之间,同名变量一般也不会相互干扰。让我们来通过一个小实验证明一下这个说法:

Bash
    location /main {
        set $var main;
        echo_location /foo;
        echo_location /bar;
        echo "main: $var";
    }
 
    location /foo {
        set $var foo;
        echo "foo: $var";
    }
 
    location /bar {
        set $var bar;
        echo "bar: $var";
    }

在这个例子中,我们分别在 /main,/foo 和 /bar 这三个 location 配置块中为同一名字的变量,$var,分别设置了不同的值并予以输出。特别地,我们在 /main 接口中,故意在调用过 /foo 和 /bar 这两个“子请求”之后,再输出它自己的 $var 变量的值。请求 /main 接口的结果是这样的:

Bash
    $ curl 'http://localhost:8080/main'
    foo: foo
    bar: bar
    main: main

显然,/foo 和 /bar 这两个“子请求”在处理过程中对变量 $var 各自所做的修改都丝毫没有影响到“主请求” /main. 于是这成功印证了“主请求”以及各个“子请求”都拥有不同的变量 $var 的值容器副本。

不幸的是,一些 Nginx 模块发起的“子请求”却会自动共享其“父请求”的变量值容器,比如第三方模块 ngx_auth_request. 下面是一个例子:

Bash
    location /main {
        set $var main;
        auth_request /sub;
        echo "main: $var";
    }
 
    location /sub {
        set $var sub;
        echo "sub: $var";
    }

这里我们在 /main 接口中先为 $var 变量赋初值 main,然后使用 ngx_auth_request 模块提供的配置指令 auth_request,发起一个到 /sub 接口的“子请求”,最后利用 echo 指令输出变量 $var 的值。而我们在 /sub 接口中则故意把 $var 变量的值改写成 sub. 访问 /main 接口的结果如下:

Bash
    $ curl 'http://localhost:8080/main'
    main: sub

我们看到,/sub 接口对 $var 变量值的修改影响到了主请求 /main. 所以 ngx_auth_request 模块发起的“子请求”确实是与其“父请求”共享一套 Nginx 变量的值容器。

对于上面这个例子,相信有读者会问:“为什么‘子请求’ /sub 的输出没有出现在最终的输出里呢?”答案很简单,那就是因为 auth_request 指令会自动忽略“子请求”的响应体,而只检查“子请求”的响应状态码。当状态码是 2XX 的时候,auth_request 指令会忽略“子请求”而让 Nginx 继续处理当前的请求,否则它就会立即中断当前(主)请求的执行,返回相应的出错页。在我们的例子中,/sub “子请求”只是使用 echo 指令作了一些输出,所以隐式地返回了指示正常的 200 状态码。

如 ngx_auth_request 模块这样父子请求共享一套 Nginx 变量的行为,虽然可以让父子请求之间的数据双向传递变得极为容易,但是对于足够复杂的配置,却也经常导致不少难于调试的诡异 bug. 因为用户时常不知道“父请求”的某个 Nginx 变量的值,其实已经在它的某个“子请求”中被意外修改了。诸如此类的因共享而导致的不好的“副作用”,让包括 ngx_echo,ngx_lua,以及 ngx_srcache 在内的许多第三方模块都选择了禁用父子请求间的变量共享。

与【转帖】nginx变量使用方法详解-5相似的内容:

【转帖】nginx变量使用方法详解-5

https://www.diewufeiyang.com/post/579.html 前面在 (二) 中我们已经了解到变量值容器的生命期是与请求绑定的,但是我当时有意避开了“请求”的正式定义。大家应当一直默认这里的“请求”都是指客户端发起的 HTTP 请求。其实在 Nginx 世界里有两种类型的“请

【转帖】nginx变量使用方法详解-1

https://www.diewufeiyang.com/post/575.html Nginx 的配置文件使用的就是一门微型的编程语言,许多真实世界里的 Nginx 配置文件其实就是一个一个的小程序。当然,是不是“图灵完全的”暂且不论,至少据我观察,它在设计上受 Perl 和 Bourne She

【转帖】nginx变量使用方法详解-2

https://www.diewufeiyang.com/post/576.html 关于 Nginx 变量的另一个常见误区是认为变量容器的生命期,是与 location 配置块绑定的。其实不然。我们来看一个涉及“内部跳转”的例子: Bash server { listen 8080; locati

【转帖】nginx变量使用方法详解-3

https://www.diewufeiyang.com/post/577.html 也有一些内建变量是支持改写的,其中一个例子是 $args. 这个变量在读取时返回当前请求的 URL 参数串(即请求 URL 中问号后面的部分,如果有的话 ),而在赋值时可以直接修改参数串。我们来看一个例子: Bas

【转帖】nginx变量使用方法详解-4

https://www.diewufeiyang.com/post/578.html 在设置了“取处理程序”的情况下,Nginx 变量也可以选择将其值容器用作缓存,这样在多次读取变量的时候,就只需要调用“取处理程序”计算一次。我们下面就来看一个这样的例子: Bash map $args $foo {

【转帖】nginx变量使用方法详解-6

https://www.diewufeiyang.com/post/580.html Nginx 内建变量用在“子请求”的上下文中时,其行为也会变得有些微妙。 前面在 (三) 中我们已经知道,许多内建变量都不是简单的“存放值的容器”,它们一般会通过注册“存取处理程序”来表现得与众不同,而它们即使有存

【转帖】nginx变量使用方法详解-7

https://www.diewufeiyang.com/post/581.html 在 (一) 中我们提到过,Nginx 变量的值只有一种类型,那就是字符串,但是变量也有可能压根就不存在有意义的值。没有值的变量也有两种特殊的值:一种是“不合法”(invalid),另一种是“没找到”(not fou

【转帖】nginx变量使用方法详解-8

https://www.diewufeiyang.com/post/582.html 与 $arg_XXX 类似,我们在 (二) 中提到过的内建变量 $cookie_XXX 变量也会在名为 XXX 的 cookie 不存在时返回特殊值“没找到”: Bash location /test { cont

[转帖]nginx中map使用方法

场景: 匹配请求 url 的参数,如果参数是 debug 则设置 $foo = 1 ,默认设置 $foo = 0 map $args $foo { default 0; debug 1;} $args 是nginx内置变量,就是获取的请求 url 的参数。 如果 $args 匹配到 debug 那么

[转帖]nginx 反向代理 URL替换方案

nginx 提供反向代理服务,日常开发过程中有时候我们需要使用nginx 作为代理服务根据url的不同去访问不同的服务器或者不同端口,如下提供两种方案。 1.直接替换location 匹配部分 1.proxy_pass的目标地址,默认不带/,表示只代理域名,url和参数部分不会变(把请求的path拼