Nginx Lua编程-复杂案例 nginx lua proxy_pass
liebian365 2024-10-29 15:55 32 浏览 0 评论
案例一:重定向与内部子请求
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后,结果如下:
后续会介绍实际生产场景中的案例~
相关推荐
- Linux-常用操作命令介绍(linux常用的命令大全)
-
1.帮助命令帮助命令1.1help命令语法格式:命令--help作用:查看某个命令的帮助信息示例#ls--help#netstat--help1.2man命令语法格式:man命令...
- 推荐:一个小而美的Java工具类库(java工具软件)
-
前言是的,你没看错,没看错,它就是hutool!相信很多做java开发的朋友应该都已经认识并使用过它了,今天带大家再重温一下它都有哪些功能,并以示例来看看hutool是如何简便实现JWT认...
- 【SpringBoot后端开发】第三部分 Linux操作系统常用命令(3)
-
创作不易,请帮忙转发、点赞和评论!四、Linux常用命令对于Linux系统来说,中央处理器、内存、磁盘驱动器、键盘、鼠标、用户等都是文件,而Linux系统管理的命令是它正常运行的核心,与之DOS命令类...
- linux常用命令在线查询工具(linux常用命令在线查询工具有哪些)
-
linuxvi编辑器常用命令linux查看iplinuxfind-name查找文件名linuxshelllinux查看端口占用linux删除文件命令linuxcp命令复制文件到另一个...
- 使用免费绿色工具chfs,将文件夹共享成网盘
-
需求:业务需求方有个需要将apk包上传到服务器中,通过chfs可以将服务器目录共享出来,可以可以登录后台自行上传apk文件包。本文就教大家三个知识点1.centos7下使用chfs,共享目录。2.使用...
- Mysql和Hive之间通过Sqoop进行数据同步
-
文章回顾理论大数据框架原理简介大数据发展历程及技术选型实践搭建大数据运行环境之一搭建大数据运行环境之二本地MAC环境配置CPU数和内存大小查看CPU数sysctlmachdep.cpu#核数为...
- 真实案例记录Linux被植入rootkit导致服务器带宽跑满的解决过程
-
一、关于linux下的rootkitrootkit是Linux平台下最常见的一种木马后门工具,它主要通过替换系统文件来达到攻击和和隐蔽的目的,这种木马比普通木马后门更加危险和隐蔽,普通的检测工...
- python周期任务调度工具Schedule使用详解
-
如果你想周期性地执行某个Python脚本,最出名的选择应该是Crontab脚本,但是Crontab具有以下缺点:不方便执行秒级任务。当需要执行的定时任务有上百个的时候,Crontab的管...
- Linux 系统日常巡检脚本(shell巡检脚本)
-
Linux系统日常巡检脚本,巡检内容包含了,磁盘,内存cpu进程文件更改用户登录等一系列的操作直接用就行了。报告以邮件发送到邮箱在log下生成巡检报告。#!/bin/bash#@Au...
- Schedule—简单实用的 Python 周期任务调度工具
-
如果你想周期性地执行某个Python脚本,最出名的选择应该是Crontab脚本,但是Crontab具有以下缺点:1.不方便执行秒级任务。2.当需要执行的定时任务有上百个的时候,Cronta...
- celery定时与异步任务详解(定时任务异步执行)
-
celery简介Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度。Celery的架构由三部分组成,消息中间件(messagebroke...
- 开源免费的定时任务管理系统:Gocron
-
Gocron:精准调度未来,你的全能定时任务管理工具!-精选真开源,释放新价值。概览Gocron是github上一个开源免费的定时任务管理系统。它使用Go语言开发,是一个轻量级定时任务集中调度和管理...
- PHP Laravel定时任务Schedule(laravel定时任务原理)
-
前提:本文方法是利用Linux的crontab定时任务来协助实现Laravel调度(Mac也一样)。一、首先添加Crontab定时任务,这里只做简单介绍:用命令crontab-e添加如下内容**...
- Linux的常用命令就是记不住,怎么办?于是推出了这套教程
-
1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...
- 如何定期执行 Python 脚本:5 种常见方法
-
定期执行任务是自动化工作流程中的重要环节,无论是数据抓取、文件备份,还是定期报告生成,定时运行脚本都可以极大提高效率。本文将介绍五种方法,通过这些方法,你可以轻松设置定期执行Python脚本的任务...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)