上一节介绍了,当有请求http://127.0.0.1/userid/play/live/01.m3u8进入时,如何通过content_by_lua进行url解析,并返回一个ts信息。但在实际使用中,ts信息一般是放在后台服务器上的。这个时候,就要在原有的请求中,发起一个子请求。
谈到子请求,我们先从capture认识子请求的基本面貌。从官方文档看到,https://github.com/openresty/lua-nginx-module#ngxlocationcapture
可以看到小demo如下,
location /other {
set $dog "$dog world";
echo "$uri dog: $dog";
}
location /lua {
set $dog 'hello';
content_by_lua_block {
res = ngx.location.capture("/other",
{ copy_all_vars = true })
ngx.print(res.body)
ngx.say(ngx.var.uri, ": ", ngx.var.dog)
}
}
好,参考上面的小demo,在上一节check.lua中,请求m3u8的地方,我们去后台请求m3u8,增加下面的逻辑,
--m[3]来自请求url中的01.m3u8部分
local reqm3u8=string.format('/upstream/%s',m[3])
ngx.log(ngx.NOTICE,reqm3u8)
--get m3u8 info from upstream by subrequest
local res = ngx.location.capture(reqm3u8,
{
method = ngx.HTTP_GET,
share_all_vars = true,
body = reqm3u8
}
)
在nginx.conf中增加下面逻辑,
upstream get_m3u8 {
server 127.0.0.1:8080;
}
location /upstream
{
set $my_upstream "get_m3u8";
rewrite_by_lua_block {
ngx.log(ngx.ERR, ngx.req.raw_header());
ngx.log(ngx.ERR,ngx.var.request_uri,ngx.var.request_body);
ngx.log(ngx.ERR,"req_body,test:",ngx.var.request_body);
local m,err=ngx.re.match(ngx.var.request_body, "/(.*)/(.*)")
local requri=string.format('/m3u8_v2/%s',m[2]);
ngx.req.set_uri(requri);
ngx.log(ngx.ERR,"requri,test:",requri);
}
proxy_pass http://$my_upstream;
}
server {
listen 8080;
location /m3u8_v2 {
# echo ngx.var.request_uri;
content_by_lua 'ngx.say(ngx.var.request_uri)';
}
location /world {
echo "world";
}
}
请求后,curl http://127.0.0.1/99/play/live/01.m3u8
/M3U8_V2/01.M3U8
即,初始请求 /99/play/live/01.m3u8,经过判断,需要执行一个m3u8的子请求操作,那么构造一个upstream/01.m3u8的子操作,重新进入location逻辑,然后进行一次proxy_pass的upstream请求。当然,在此location中,因为后端服务器也用nginx模拟的(下一节制作一个真实的后台server),构造了一个8080端口拉起的服务,m3u8_v2服务。8080这个服务不是必须的,可以用sock服务或者python起一个非nginx的server。
这里遇到了几个细节需要关注:
1、子请求执行location /upstream中如何获取subrequest请求url
因为ngx.location.capture中,构造的subrequest,会把初始的req_uri信息 99/play/live/01.m3u8,原封不动的传导到子请求执行location /upstream中。此时ngx.var.request_uri的信息如下:
2021/02/14 06:31:27 [error] 6607#0: *122 [lua] rewrite_by_lua(nginx.conf:64):4: req_body,test:/upstream/01.m3u8, client: 127.0.0.1, server: localhost, request: "GET /99/play/live/01.m3u8 HTTP/1.1", subrequest: "/upstream/01.m3u8", host: "127.0.0.1"
那么,就需要在capture中,增加一个body的声明,body = reqm3u8,输出如上述日志,/upstream/01.m3u8
拿到subrequest操作后,就可以rewrite一下url, ngx.req.set_uri(requri);
当然,如果是固定的url,参数从args里面取的,也可以在通过rewrite ^/upstream/(.*) /m3u8_v2/$1 ;直接重写url
2、ngx.log 打印中间变量的问题
https://www.iteye.com/blog/siyuan-zhu-2231016帖子给出一个很好的使用content语法的建议。
总结即,content_by_lua、proxy_pass、echo同属于content阶段函数,不能在一个location中同时使用。
那么我们在subrequest中用到了proxy_pass,就不能使用content_by_lua和echo。再考察其他lua执行阶段,比如 rewrite、access. 因为子请求不能使用access,所以,最终使用rewrite_by_lua_block ,来进行一些ngx.log的操作。