-
Notifications
You must be signed in to change notification settings - Fork 8
第十三章、子请求
Nginx一般分两种请求类型,一种是主请求;一种是子请求,即subrequest。主请求从Nginx的外部进行访问,而子请求则在Nginx内部进行访问。子请求不是HTTP请求,不会增加网络开销。它的主要作用是将一个主请求分解为多个子请求,用子请求去访问指定的location服务,最后汇总到一起完成主请求的任务。
Nginx的请求方法有很多种,如GET、POST、 PUT 、DELETE等,同样,子请求也支持这些请求方法。
Lua API中提供了多个指令来实现子请求,Lua API常见的请求方法说明见表7-2。 表7-2 Lua API常见的请求方法说明
语法:res = ngx.location.capture(uri, options?)
配置环境:rewrite_by_lua*,access_by_lua*,content_by_lua*
含义:发出同步但不阻塞Nginx的子请求。可以用来访问指定的location,但不支持访问命名location(如@abc 就是命名location)。location中可以有静态文件,如ngx_proxy、ngx_fastcgi、ngx_memc、ngx_postgres、ngx_drizzle,甚至是Ngx_Lua和Nginx的c模块。 子请求总是会把整个请求体缓存到内存中,如果要处理一个较大的子请求,使用cosockets是最好的选择(cosockets是与ngx.socket.tcp有关的API)。 子请求一般在内部进行访问,建议在被子请求访问的location上配置internal,即只允许内部访问。 子请求返回的结果res,它是一个table类型的数据,包含4个元素:res.status、res.header、res.body和res.truncated,res的元素名及其用途见表7-3。
表7-3 res的元素名及其用途
ngx.location.capture的第2个参数options是可选参数,也可以包含多个参数,示例如下: server { listen 80; server_name testnginx.com; default_type 'text/plain';
location = /main {
set $m 'hello';
content_by_lua_block {
local ngx = require "ngx";
--发起子请求,访问/test,请求方式是GET,请求体是test nginx,子请求的URL参数是a=1&b=2,并使用copy_all_vars将主请求的Nginx变量($m)全部复制到子请求中
local res = ngx.location.capture(
'/test', { method = ngx.HTTP_GET , body = 'test nginx',
args = { a = 1, b = 2 },copy_all_vars = true }
)
ngx.say(res.status)
ngx.say(res.body)
ngx.say(type(res.header))
ngx.say(type(res.truncated))
}
}
location = /test
{ #只能在Nginx内部进行访问 internal; content_by_lua_block { local ngx = require "ngx"; --获取请求体,在这里是获取主请求的请求体 ngx.req.read_body() local body_args = ngx.req.get_body_data() --输出请求的参数,获取主请求的m变量的值,并与world进行字符串拼接 ngx.print('request_body: ' ,body_args, ' capture_args: ', ngx.var.args, '--- copy_all_vars : ', ngx.var.m .. 'world! ') } } }
执行结果如下:
# curl 'http://testnginx.com/main'
200
request_body:test nginx capture_args:a=1&b=2--- copy_all_vars : helloworld!
table
boolean
从示例中可以看出:
1.ngx.location.capture的第2个参数options可以包含多个table类型的参数。
2.子请求的请求方法由参数method进行配置,示例中的请求方法为GET。
3.子请求通过参数body可以定义新的请求体。
4.子请求通过参数args可以配置新的URL的args,args是table类型的。
5.copy_all_vars = true的作用是将主请求的全部变量传递给子请求,如果没有此配置就不会传递过去。
6.从子请求的返回结果可以获取状态码、响应体、响应头、结果是否被截断。
根据上面的介绍可知,下面两种方式是等价的: local res = ngx.location.capture('/test?a=1&b=2') local res = ngx.location.capture('/test , args = { a = 1, b = '2' }') ngx.location.capture 还支持更丰富的参数操作,具体如下。
1.vars参数,table类型,可以设置子请求中的变量值,前提是该变量在Nginx中被声明过。如果配置copy_all_vars = true,且vars里有和主请求相同的变量,则会使用vars中变量的值;如果vars里是新变量,就会和主请求的变量一起传递过去。
2.share_all_vars参数,用来共享主请求和子请求的变量,如果在子请求中修改了共享变量的值,主请求的变量值也会被改变。不推荐使用此参数,因为可能会导致很多意外问题的出现。
3.always_forward_body参数,默认值为false,此时,如果不设置body参数,且请求方法是PUT或POST,则主请求的请求体可以传给子请求。如果把always_forward_body设置为 true,且不设置body参数,无论请求方法是什么,主请求的请求体都会传给子请求。
4.ctx参数,指定一个table作为子请求的ngx.ctx表,它可以使主请求和子请求共享请求头的上下文环境。
关于参数vars的使用方式,示例如下: location = /main { set $m 'hello'; set $mm ''; content_by_lua_block { local ngx = require "ngx"; local res = ngx.location.capture( '/test', { method = ngx.HTTP_POST , vars = {mm = 'MMMMM',m = 'hhhh'}} ) ngx.say(res.body) } } location = /test { content_by_lua_block { local ngx = require "ngx"; ngx.print(ngx.var.m .. ngx.var.mm ) } } 执行结果如下:
# curl 'http://testnginx.com/main'
hhhhMMMMM
主请求的变量在子请求中被修改了,并传给了子请求指定的/test:
注意:使用ngx.location.capture发送子请求时,默认会将主请求的请求头全部传入子请求中,这可能会带来一些不必要的麻烦。例如,如果浏览器发送的压缩头Accept-Encoding:gzip被传入子请求中,且子请求是ngx_proxy的标准模块,则请求的结果会被压缩后再返回,导致Lua无法读取子请求返回的数据。因此应将子请求的 proxy_pass_request_headers设置为off,避免把请求头传递给后端服务器。
有时需要发送多条子请求去获取信息,这时,就要用到并发操作了。
语法:res1, res2, ... = ngx.location.capture_multi({ {uri, options?}, {uri, options?}, ... })
配置环境:rewrite_by_lua*,access_by_lua*,content_by_lua*
含义:与ngx.location.capture相似,但可以支持多个子请求并行访问,并按配置顺序返回数据。返回的数据也是多个结果集。 示例:
server { listen 80; server_name testnginx.com; default_type 'text/plain'; location = /main { set $m 'hello'; set $mm ''; content_by_lua_block { local ngx = require "ngx"; --发送两个子请求,会返回两个结果集 local res1, res2 = ngx.location.capture_multi{ { "/test1?a=1&b=2" }, { "/test2",{ method = ngx.HTTP_POST},body = "test nginx" }, } --返回的body的方式和ngx.location.capture一样 if res1.status == ngx.HTTP_OK then ngx.say(res1.body) end
if res2.status == ngx.HTTP_OK then
ngx.say(res2.body)
end
}
}
location = /test1 {
echo 'test1';
}
location = /test2 {
echo 'test2';
}
}
执行结果如下:
# curl 'http://testnginx.com/main'
test1
test2
主请求需要等到所有的子请求都返回后才会结束子请求的执行,最慢的子请求的执行时间就是整体的消耗时间,所以在实际业务中需要对子请求的超时时间做好限制。
注意:Nginx对子请求有并发数量限制,目前Nginx 1.1以上的版本限制子请求并发数量为200个,老版本是50个。