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

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

liebian365 2024-10-29 15:55 11 浏览 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后,结果如下:


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

相关推荐

快递查询教程,批量查询物流,一键管理快递

作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...

一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递

对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...

快递查询单号查询,怎么查物流到哪了

输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...

3分钟查询物流,教你一键批量查询全部物流信息

很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...

快递单号查询,一次性查询全部物流信息

现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...

快递查询工具,批量查询多个快递快递单号的物流状态、签收时间

最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...

快递查询软件,自动识别查询快递单号查询方法

当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...

教你怎样查询快递查询单号并保存物流信息

商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...

简单几步骤查询所有快递物流信息

在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...

物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号

最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...

连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息

快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...

快递查询教程,快递单号查询,筛选更新量为1的单号

最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...

掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析

在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...

从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息

在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...

物流单号查询,在哪里查询快递

如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...

取消回复欢迎 发表评论: