为了让Nginx高效地处理复杂请求,子请求机制用好了能事半功倍,用砸了则后患无穷。
1. 什么是子请求:外部请求与内部调用的艺术
在Nginx世界里,有两种不同类型的“请求”:一种是由客户端从Nginx外部发起的“主请求”(main request),另一种则是由Nginx正在处理的请求在Nginx内部发起的“子请求”(subrequest)。
简单来说,主请求是外部客户端发起的真实HTTP请求,而子请求则是Nginx内部的一种抽象调用,和HTTP协议乃至网络通信一点儿关系都没有。
子请求在外观上很像HTTP请求,但实现上却是Nginx内部的一种高效抽象。它的目的是为了方便用户把“主请求”的任务分解为多个较小粒度的“内部请求”,并发或串行地访问多个location接口。
举个例子,假设你要构建一个首页,需要展示用户信息、最新文章和热门话题。你可以创建三个独立的location,分别处理这三块内容,然后在主location中通过子请求将它们组合起来。
子请求的优势在于它的执行效率极高。因为这种通信是在同一个Nginx实例内部进行的,所以Nginx核心在实现“子请求”的时候,只调用了若干个C函数,完全不涉及任何网络或者UNIX套接字(socket)通信。
2. 子请求的核心运行机制
2.1 父子请求的关系
当一个请求发起一个“子请求”的时候,按照Nginx的术语,习惯把前者称为后者的“父请求”(parent request)。值得一提的是,Apache服务器中其实也有“子请求”的概念。
“子请求”的概念是相对的,任何一个“子请求”也可以再发起更多的“子子请求”,甚至可以玩递归调用(即自己调用自己)。这就好比公司里的项目组,项目经理(主请求)可以把任务分解给多个组员(子请求),而每个组员如果任务太重,还可以进一步分解给其他人(子子请求)。
2.2 变量值容器的生命期
在前面系列文章中我们已经了解到,变量值容器的生命期是与请求绑定的 。每个请求都有所有变量值容器的独立副本,即便是父子请求之间,同名变量一般也不会相互干扰。
让我们通过一个例子来验证这个说法:
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接口的结果是这样的:
$ curl 'http://localhost:8080/main'
foo: foo
bar: bar
main: main
显然,/foo和/bar这两个“子请求”在处理过程中对变量$var各自所做的修改都丝毫没有影响到“主请求”/main。这成功印证了“主请求”以及各个“子请求”都拥有不同的变量值容器副本。
2.3 不幸的例外:ngx_auth_request模块
然而,并非所有模块发起的子请求都遵循这个规则。一些Nginx模块发起的“子请求”会自动共享其“父请求”的变量值容器,比如第三方模块ngx_auth_request。
下面是一个例子:
location /main {
set $var main;
auth_request /sub;
echo "main: $var";
}
location /su

最低0.47元/天 解锁文章
633

被折叠的 条评论
为什么被折叠?



