百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

Nginx Lua编程-复杂案例 nginx lua proxy_pass

liebian365 2024-10-29 15:55 28 浏览 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容器,有如下可设置的选项

  1. method: 子请求的方法,默认为ngx.HTTP_GET,该属性只接收Nginx Lua内部定义的请求类型的常量;
  2. body: 传给子请求的请求体,支持string、nil;
  3. args: 传给子请求的参数,支持string、table。指的是HTTP请求中的参数;
  4. vars: 传给子请求的变量表,仅限于table。指的是Nginx中的自定义变量,必须提前定义,在子请求中通过ngx.var.NAME来获取;
  5. ctx: 父子请求共享的变量表table;
  6. copy_all_vars: 复制所有变量给子请求;
  7. share_all_vars: 父子请求共享所有变量;
  8. 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后,结果如下:


后续会介绍实际生产场景中的案例~

相关推荐

4万多吨豪华游轮遇险 竟是因为这个原因……

(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...

“菜鸟黑客”必用兵器之“渗透测试篇二”

"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...

科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白

作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...

麦子陪你做作业(二):KEGG通路数据库的正确打开姿势

作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...

知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势

智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...

每日新闻播报(September 14)_每日新闻播报英文

AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...

香港新巴城巴开放实时到站数据 供科技界研发使用

中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...

5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper

本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...

Qt动画效果展示_qt显示图片

今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...

如何从0到1设计实现一门自己的脚本语言

作者:dong...

三年级语文上册 仿写句子 需要的直接下载打印吧

描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...

C++|那些一看就很简洁、优雅、经典的小代码段

目录0等概率随机洗牌:1大小写转换2字符串复制...

二年级上册语文必考句子仿写,家长打印,孩子照着练

二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...

一年级语文上 句子专项练习(可打印)

...

亲自上阵!C++ 大佬深度“剧透”:C++26 将如何在代码生成上对抗 Rust?

...

取消回复欢迎 发表评论: