网站首页 > 技术教程 正文
案例一:重定向与内部子请求
Nginx Lua内部重定向
语法格式:
ngx.exec(uri, args?)
等价于下面的rewrite指令:
rewrite regrex replacement last;
使用注意事项:
- 如果有args参数,参数可以是字符串的形式,也可以是Lua table的形式
- 该方法可能不会主动返回,建议在调用该方法时显示加上return
实战示例:
#使用ngx.exec进行内部重定向
location /internal/sum {
internal;
content_by_lua_block {
local arg_a = tonumber(ngx.var.arg_a);
local arg_b = tonumber(ngx.var.arg_b);
local arg_c = tonumber(ngx.var.arg_c);
local sum = arg_a + arg_b + arg_c;
ngx.say(arg_a, "+", arg_b, "+", arg_c, "=", sum);
}
}
location /sum {
content_by_lua_block {
-- 内部重定向到/internal/sum,并传递一个Lua table参数
return ngx.exec("/internal/sum", {a = 100, b = 10, c = 1});
}
}
浏览器访问http://localhost/sum,结果为:
Nginx Lua外部重定向
语法格式:
ngx.rewrite(uri, status?)
外部重定向将通过客户端进行二次跳转,所以ngx.rewrite方法会产生额外的流量,该方法的第二个参数为响应状态码,可以传递301/302/303/307/308重定向状态码。若不指定status值,该方法的默认响应状态码为302,表示临时重定向。
需要注意的是,ngx.rewrite方法也不会主动返回,使用时建议加上return。
实战示例:
#最终访问目标URL:https://www.zhihu.com/question/268589944/answer/1914827166
# 使用 location 指令后面的正则表达式进行URL后缀捕获
#http://localhost/redirectTest/question/268589944/answer/1914827166
location ~* /redirectTest/(.*) {
content_by_lua_block {
-- 使用 ngx.redirect 方法进行外部重定向
-- 请求URI为正则捕获组1
return ngx.redirect("https://www.zhihu.com/"..ngx.var[1]);
}
}
#http://localhost/redirectTest1/question/268589944/answer/1914827166
location ~* /redirectTest1/* {
# 使用 rewrite 指令后面的正则表达式进行URL后缀捕获
rewrite ^/redirectTest1/(.*) $1 break;
content_by_lua_block {
-- 使用 ngx.redirect 方法进行外部重定向
-- 博客参数为 正则捕获组1
return ngx.redirect("https://www.zhihu.com/"..ngx.var[1]);
}
}
#http://localhost/redirectTest2/question/268589944/answer/1914827166
location ~* /redirectTest2/* {
# 使用 rewrite 指令进行外部重定向
rewrite ^/redirectTest2/(.*) https://www.zhihu.com/$1 redirect;
}
重启OpenResty后,浏览器端分别访问如下地址:
http://localhost/redirectTest/question/268589944/answer/1914827166
http://localhost/redirectTest1/question/268589944/answer/1914827166
http://localhost/redirectTest2/question/268589944/answer/1914827166
案例二:ngx.location.capture子请求
Nginx子请求并非HTTP协议的标准实现,而是Nginx特有的设计,主要是为了提高内部对单个客户端请求处理的并发能力。子请求并不是由客户端直接发起,它是由Nginx服务器在处理客户端请求时根据自身逻辑需要而内部建立的新请求。因此,子请求只在Nginx服务器内部处理,不会与客户端进行交互。若需要发起外部HTTP路径的子请求,就需要与location或者upstream反向代理配置配合使用。
通常情况下,为了保护子请求所定义的内部接口,会把这些接口设置为internal,防止外部直接访问。
发起单个子请求的格式:
ngx.location.capture(uri, options?);
参数options是一个table容器,有如下可设置的选项
- method: 子请求的方法,默认为ngx.HTTP_GET,该属性只接收Nginx Lua内部定义的请求类型的常量;
- body: 传给子请求的请求体,支持string、nil;
- args: 传给子请求的参数,支持string、table。指的是HTTP请求中的参数;
- vars: 传给子请求的变量表,仅限于table。指的是Nginx中的自定义变量,必须提前定义,在子请求中通过ngx.var.NAME来获取;
- ctx: 父子请求共享的变量表table;
- copy_all_vars: 复制所有变量给子请求;
- share_all_vars: 父子请求共享所有变量;
- always_forward_body: 用于设置是否转发请求体,true表示父请求中的请求体request body将转发到子请求。
实战示例:
- 外部访问接口:/goods/detail/88?foo=bar
- 内部访问接口:/internal/detail/88
外部接口专供外部访问,在准备好必要的请求参数、上下文环境变量、请求体后,调用内部访问接口获取执行结果,然后返回给客户端。
#ngx.location.capture示例
#向外公开的请求
location ~ /goods/detail/([0-9]+) {
set $goodsId $1; #将location的正则捕获组1,赋值到变量 $goodsId
set $var1 '';
set $var2 '';
set $father_method '';
content_by_lua_block {
-- 解析body参数之前一定要先获取request body
ngx.req.read_body();
-- 组装内部请求uri
local uri = "/internal/detail/"..ngx.var.goodsId;
local request_method = ngx.var.request_method;
ngx.say("父请求方法是: ", request_method.."<br>");
ngx.say("request_method类型: ", type(request_method).."<br>");
-- 获取父请求的参数:假设父请求是GET请求
local args = ngx.req.get_uri_args();
--local vars = {var1 = "value1", var2 = "value2", father_method = request_method};
-- 定义主子请求共享变量
local shareCtx = {c1 = "v1", other = "other value"};
-- 发送单个子请求
local res = ngx.location.capture(uri, {
-- 用于指定子请求为GET请求
method = ngx.HTTP_GET,
--将父请求的参数转发给子请求
args = args,
body = 'customed request body',
--传递Nginx中的自定义变量
vars = {var1 = "value1", var2 = "value2", father_method = request_method},
--转发父请求的request body
always_forward_body = true,
--共享给子请求的上下文table
ctx = shareCtx,
});
ngx.say(" child res.status: ", res.status);
ngx.say(res.body);
ngx.say("<br>shareCtx.c1 = ", shareCtx.c1);
}
}
#内部接口请求
location ~ /internal/detail/([0-9]+) {
internal; #此指令限制外部客户端是不能直接访问内部接口
set $goodsId $1; #将捕获组1的值,放置到Nginx自定义变量 goodsId中
content_by_lua_block {
local basic = require("conf.luaScript.module.common.basic");
ngx.req.read_body();
ngx.say("<br><hr>child start: ");
--访问父请求传递的参数
local args = ngx.req.get_uri_args();
ngx.say("<br> 父请求传递的请求参数:foo = ", args.foo);
--访问父请求传递的请求体
local data = ngx.req.get_body_data();
ngx.say(", <br> 父请求传递的请求体:data = ", data);
--访问自定义的Nginx变量
ngx.say(", <br> 获取Nginx自定义变量:goodsId = ", ngx.var.goodsId);
--访问父请求传递的变量
ngx.say(", <br> 父请求传递的变量:var.var1 = ", ngx.var.var1);
ngx.say(", <br> 父请求传递的变量:var.father_request = ", ngx.var.father_method);
--访问父请求传递的共享上下文,并修改其属性
ngx.say(", <br> ngx.ctx.c1 = ", ngx.ctx.c1);
ngx.say("<br>child end<hr>");
ngx.ctx.c1 = "changed value by child";
}
}
访问http://localhost/goods/detail/88?foo=bar,运行结果:
案例三:ngx.location.capture_multi并发子请求
微服务架构下,后台系统将会提供大量的细粒度接口,一次客户端请求往往调用多个微服务接口才能获取到完整的页面内容。这种场景下可以通过网关进行上游接口合并,减少前后端的交互次数。
在OpenResty中,ngx.location.capture_multi可以用于上游接口合并的场景,该方法可以完成内部多个子请求和并发访问。格式如下:
ngx.location.capture_multi({uri, options?}, {uri, options?}, ...);
capture_multi可以一次发送多个内部子请求,每一个子请求的参数使用方式与capture方法相同。调用capture_multi前可以把所有的子请求加入一个table容器表中,作为调用参数传入;capture_multi返回后可以将其结果再用花括号{}包装成一个table,方便后面的迭代处理。
在所有子请求终止之前,ngx.location.capture_multi(...)函数不会返回。此函数的耗时是单个子请求的最长延迟,而不是所有子请求的耗时总和,因为所有子请求是并发执行的。
实战示例:
#通过capture_multi方法一次并发地请求两个内部接口
#发起两个子请求:一个get请求,一个post请求
location /capture_multi_demo {
content_by_lua_block {
local postBody = ngx.encode_args({post_k1 = 32, post_k2 = "post_v2"});
local reqs = {};
table.insert(reqs, {"/print_get_param", {args = "a=3&b=4"}});
table.insert(reqs, {"/print_post_param", {method = ngx.HTTP_POST, body = postBody}});
-- 统一发请求,等待结果
local resps = {ngx.location.capture_multi(reqs)};
-- 迭代结果列表
for i, res in ipairs(resps) do
ngx.say(" child res.status: ", res.status, "<br>");
ngx.say(" child res.body: ", res.body, "<br>");
end
}
}
#模拟上游接口1:输出get请求参数
location /print_get_param {
internal;
content_by_lua_block {
ngx.say("<br><hr>child start: ");
local arg = ngx.req.get_uri_args();
for k, v in pairs(arg) do
ngx.say("<br>[GET] key: ", k, " value: ", v);
end
ngx.say("<br>child end <hr>");
}
}
#模拟上游接口2:输出post请求参数
location /print_post_param {
internal;
content_by_lua_block {
ngx.say("<br><hr> child start: ");
ngx.req.read_body(); --解析body之前一定要先读取body
local arg = ngx.req.get_post_args();
for k, v in pairs(arg) do
ngx.say("<br>[POST] key: ", k, " value: ", v);
end
ngx.say("<br>child end <hr>");
}
}
浏览器访问http://localhost/capture_multi_demo后,结果如下:
后续会介绍实际生产场景中的案例~
猜你喜欢
- 2024-10-09 openresty代替nginx并使用lua扩展功能
- 2024-10-09 Redis弱事务性与Lua脚本原子性分析
- 2024-09-08 基于Nginx+lua的蓝绿发布系统(lvs与nginx)
- 2024-09-08 Go Web 框架 Gin 实践17—用 Nginx 部署 Go 应用
- 2024-09-08 一文看懂灰度发布——基于Nginx+Lua+Redis
- 2024-09-08 Lua 基础入门(lua经典详细入门教程中文pdf)
- 2024-09-08 Redis中使用Lua脚本来实现并发下的原子操作
- 2024-09-08 运维篇—基于Nginx+Lua实现的灰度发布
- 2024-09-08 Nginx+Lua+Redis实现高性能缓存数据读取
- 2024-09-08 OpenResty实战-Lua入门-Lua模块(openresty教程)
你 发表评论:
欢迎- 最近发表
-
- Win11学院:如何在Windows 11上使用WSL安装Ubuntu
- linux移植(Linux移植freemodbus)
- 独家解读:Win10预览版9879为何无法识别硬盘
- 基于Linux系统的本地Yum源搭建与配置(ISO方式、RPM方式)
- Docker镜像瘦身(docker 减小镜像大小)
- 在linux上安装ollama(linux安装locale)
- 渗透测试系统Kali推出Docker镜像(kali linux渗透测试技术详解pdf)
- Linux环境中部署Harbor私有镜像仓库
- linux之间传文件命令之Rsync傻瓜式教程
- 解决ollama在linux中安装或升级时,通过国内镜像缩短安装时长
- 标签列表
-
- 下划线是什么 (87)
- 精美网站 (58)
- qq登录界面 (90)
- nginx 命令 (82)
- nginx .http (73)
- nginx lua (70)
- nginx 重定向 (68)
- Nginx超时 (65)
- nginx 监控 (57)
- odbc (59)
- rar密码破解工具 (62)
- annotation (71)
- 红黑树 (57)
- 智力题 (62)
- php空间申请 (61)
- 按键精灵 注册码 (69)
- 软件测试报告 (59)
- ntcreatefile (64)
- 闪动文字 (56)
- guid (66)
- abap (63)
- mpeg 2 (65)
- column (63)
- dreamweaver教程 (57)
- excel行列转换 (56)
本文暂时没有评论,来添加一个吧(●'◡'●)