openresty开发系列29--openresty中发起http请求
openresty开发系列29--openresty中发起http请求
有些场景是需要nginx在进行请求转发
用户浏览器请求url访问到nginx服务器,但此请求业务需要再次请求其他业务;
如用户请求订单服务获取订单详情,可订单详情中需要返回商品信息,也就需要再请求商品服务获取商品信息;
这样就需要nginx需要有发起http请求的能力,而不是让用户浏览器再次请求商品信息
nginx服务发起http请求区分内部请求 和 外部请求
图解
下面我们就介绍一下,openResty中如何发起http请求?
一)内部请求
1)capture请求方法
res = ngx.location.capture(uri,{
options?
});
options可以传参数和设置请求方式
local res = ngx.location.capture("/product",{
method = ngx.HTTP_GET, #请求方式
args = {a=1,b=2}, #get方式传参数
body = "c=3&d=4" #post方式传参数
});
res.status --->保存子请求的响应状态码
res.header --->用一个标准 Lua 表储子请求响应的所有头信息。如果是"多值"响应头,
--->这些值将使用 Lua (数组) 表顺序存储。
res.body --->保存子请求的响应体数据,它可能被截断。
--->用户需要检测 res.truncated (截断) 布尔值标记来判断 res.body 是否包含截断的数据。
--->这种数据截断的原因只可能是因为子请求发生了不可恢复的错误,
--->例如远端在发送响应体时过早中断了连接,或子请求在接收远端响应体时超时。
res.truncated --->是否截断
-----------------------------------------
编辑nginx.conf配置文件,配置一下路由,定义有个两个服务请求 商品服务请求和订单服务请求
location /product { #商品服务请求
echo "商品请求";
}
location /order { #订单服务请求
content_by_lua_block {
local res = ngx.location.capture("/product");
ngx.say(res.status)
ngx.say(res.body)
}
}
ngx.location.capture 方法就是发起http的请求,但是它只能请求 内部服务,不能直接请求外部服务
输出结果,http状态为200,返回了 商品服务中的内容
------------------------------------------------
这边有一种情况,这样的定义,用户用浏览器直接请求商品服务也照样请求
可很多时候我们会要求商品请求 是不对外暴露的,也就是用户无法直接访问商品服务请求。
那我们只要在内部请求那边加上一个关键字,internal 就可以了
location /product { #商品服务请求
internal;
echo "商品请求";
}
这样直接访问就报404错误了
-----------------post 请求-----------------
location /product { #商品服务请求
content_by_lua_block {
ngx.req.read_body();
local args = ngx.req.get_post_args()
ngx.print(tonumber(args.a) + tonumber(args.b))
}
}
location /order { #订单服务请求
content_by_lua_block {
local res = ngx.location.capture("/product",{
method = ngx.HTTP_POST,
args = {a=1,b=2},
body = "a=3&b=4"
});
ngx.say(res.status)
ngx.say(res.body)
}
}
2)capture_multi 并发请求
再以上基础上面增加需求,要获得用户信息
正常逻辑是串行: order request ---> product request ---> user request ----> end
提高性能的方式,发送product请求的同时发起user请求: order request ---> product request
---> user request ----> end
语法:res1,res2, ... = ngx.location.capture_multi({
{uri, options?},
{uri, options?},
...
})
-----并发调用
location = /sum {
internal;
content_by_lua_block {
ngx.sleep(0.1)
local args = ngx.req.get_uri_args()
ngx.print(tonumber(args.a) + tonumber(args.b))
}
}
location = /subduction {
internal;
content_by_lua_block {
ngx.sleep(0.1)
local args = ngx.req.get_uri_args()
ngx.print(tonumber(args.a) - tonumber(args.b))
}
}
location = /app/test_multi {
content_by_lua_block {
local start_time = ngx.now()
local res1, res2 = ngx.location.capture_multi( {
{"/sum", {args={a=3, b=8}}},
{"/subduction", {args={a=3, b=8}}}
})
ngx.say("status:", res1.status, " response:", res1.body)
ngx.say("status:", res2.status, " response:", res2.body)
ngx.say("time used:", ngx.now() - start_time)
}
}
location = /app/test_queue {
content_by_lua_block {
local start_time = ngx.now()
local res1 = ngx.location.capture("/sum", {
args={a=3, b=8}
})
local res2 = ngx.location.capture("/subduction", {
args={a=3, b=8}
})
ngx.say("status:", res1.status, " response:", res1.body)
ngx.say("status:", res2.status, " response:", res2.body)
ngx.say("time used:", ngx.now() - start_time)
}
}
访问:http://10.11.0.215/app/test_queue
返回:status:200 response:11 status:200 response:-5 time used:0.22399997711182
访问:http://10.11.0.215/app/test_multi
返回:status:200 response:11 status:200 response:-5 time used:0.10199999809265
从处理的时间长度看multi并发比queue队列要快一倍左右
二)外部请求
如何发起外部请求呢?
因为ngx.location.capture不能直接发起外部请求,我们需要通过内部请求中用反向代理请求发起外部请求
请求tmall和淘宝搜索的时候直接通过浏览器可以搜索,但是使用反向代理后无法正常返回内容,天猫和淘宝做了反爬虫处理
location /tmall {
internal;
proxy_pass "https://list.tmall.com/search_product.htm?q=ipone";
}
location /ordertmall {
content_by_lua_block {
local res = ngx.location.capture("/tmall");
ngx.say(res.status)
ngx.say(res.body)
}
}
类似的例子:
location /baidu {
internal;
# 防止返回乱码
proxy_set_header Accept-Encoding ' ';
proxy_pass "https://www.baidu.com/s?wd=iphone";
#proxy_pass "https://www.baidu.com";
}
location /orderbaidu {
content_by_lua_block {
local res = ngx.location.capture("/baidu");
ngx.say(res.status)
ngx.say(res.body)
}
}
在商品服务那边用的proxy_pass 请求外部http请求,这样就达到了请求外部http的目的。
请求返回了302,表示请求成功了;
但发现都是乱码,这个是什么原因呢?
一开始想到的是字符编码的问题,需要把虚拟主机的server模块配置一个字符编码
charset UTF-8; 设置为utf-8。重启nginx重新访问
还是乱码,这是为什么呢,编码不是改了吗?这个是因为baidu这个web服务器加了gzip压缩,
他返回给我们的结果是经过压缩的,我们再接受过来的时候需要解压才行,那怎么办?
我们可以让taobao服务返回不需要压缩的数据吗? 我们可以在请求外部链接那边设置
proxy_set_header Accept-Encoding ' ';#让后端不要返回压缩(gzip或deflate)的内容
最终
location /baidu {
internal;
# 防止返回乱码
proxy_set_header Accept-Encoding ' ';
proxy_pass "https://www.baidu.com/s?wd=iphone";
#proxy_pass "https://www.baidu.com";
}
重启nginx,再次访问,结果输出
以上我们介绍了 内部访问 和 外部访问
三)动态变量
刚才我们请求外部请求,是写死了wd=iphone,那我们用capture传参
location /baidu02 {
internal;
resolver 8.8.8.8;
proxy_set_header Accept-Encoding ' ';
proxy_pass "https://www.baidu.com/s?wd=$arg_wd";
}
location /orderbaidu02 {
content_by_lua_block {
local get_args = ngx.req.get_uri_args();
local res = ngx.location.capture("/baidu02",{
method = ngx.HTTP_GET,
args = {wd=get_args["wd"]}
});
ngx.say(res.status)
ngx.say(res.body)
}
}
访问:http://192.168.10.164/orderbaidu02?wd=huawei
添加搜索参数 wd=huawei就会返回华为手机的结果
注意:在proxy_pass 中使用变量,需要使用resolver指令解析变量中的域名
# google 域名解析
resolver 8.8.8.8;
这样我们请求传wd参数的值,随便由用户决定查询什么值。
我们这边就发现了请求外部服务的时候发现比较复杂,我们可以借用第三方的库 resty.http ,
从可实现外部请求,而且使用很方便,下篇文章我们就介绍resty.http模块
openresty开发系列29--openresty中发起http请求的更多相关文章
- openresty开发系列24--openresty中lua的引入及使用
openresty开发系列24--openresty中lua的引入及使用 openresty 引入 lua 一)openresty中nginx引入lua方式 1)xxx_by_lua ---> ...
- openresty开发系列30--openresty中使用全局缓存
openresty开发系列30--openresty中使用全局缓存 Nginx全局内存---本地缓存 使用过如Java的朋友可能知道如Ehcache等这种进程内本地缓存.Nginx是一个Master进 ...
- openresty开发系列28--openresty中操作mysql
openresty开发系列28--openresty中操作mysql Mysql客户端 应用中最常使用的就是数据库了,尤其mysql数据库,那openresty lua如何操作mysql呢? ...
- openresty开发系列27--openresty中封装redis操作
openresty开发系列27--openresty中封装redis操作 在关于web+lua+openresty开发中,项目中会大量操作redis, 重复创建连接-->数据操作-->关闭 ...
- openresty开发系列26--openresty中使用redis模块
openresty开发系列26--openresty中使用redis模块 在一些高并发的场景中,我们常常会用到缓存技术,现在我们常用的分布式缓存redis是最知名的, 操作redis,我们需要引入re ...
- openresty开发系列25--openresty中使用json模块
openresty开发系列25--openresty中使用json模块 web开发过程中,经常用的数据结构为json,openresty中封装了json模块,我们看如何使用 一)如何引入cjson模块 ...
- openresty开发系列16--lua中的控制结构if-else/repeat/for/while
openresty开发系列16--lua中的控制结构if-else/repeat/for/while 一)条件 - 控制结构 if-else if-else 是我们熟知的一种控制结构.Lua 跟其他语 ...
- openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息
openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息 为了实现业务系统针对不同地区IP访问,展示包含不同地区信息的业务交互界面.很多情况下系统需要根据用户访问的IP信息 ...
- openresty开发系列35--openresty执行流程之5内容content阶段
openresty开发系列35--openresty执行流程之5内容content阶段 content 阶段 ---init阶段---重写赋值---重写rewrite---access content ...
随机推荐
- RQM — 需求驱动的测试管理工具
嵌入式系统复杂程度越来越高,随之而来的测试要求和任务也越来越繁重,而测试更多的是对产品满足需求情况的测试,因此,在高强度.高频度的测试过程中,难免有需求遗漏.回归测试不充分.缺陷管理不合理.测试人员疏 ...
- SpringCloud学习心得之Eureka注册中心的基本使用
SpringCloud学习心得——Eureka注册中心 示范代码链接 定义 SpringCloud Eureka是 SpringCloud Netflix微服务套件的一部分,基于 REST 的服务 ...
- Spring -11 -单例设计模式 -懒汉式/饿汉式(idea默认的)
单例设计模式 作用: 在应用程序有保证最多只能有一个实例. 好处: 2.1 提升运行效率. 2.2 实现数据共享. 案例:application 对象 懒汉式 3.1 对象只有被调用时才去创建. 3. ...
- LG3768 简单的数学题
P3768 简单的数学题 题目描述 输入一个整数n和一个整数p,你需要求出$(\sum_{i=1}^n\sum_{j=1}^n ijgcd(i,j))~mod~p$,其中gcd(a,b)表示a与b的最 ...
- final修饰的变量引用不能变还是对象不可变
两种情况:如果是基本数据类型,被final修饰的变量一旦初始化就不能改变:如果是引用数据类型的变量,初始化之后不能指向另外一个对象. 基本数据类型: package cn.yqg.day2; publ ...
- Samba服务安装
安装Samba服务 1.在可以联网的机器上使用yum工具安装,如果未联网,则挂载系统光盘进行安装. # yum install samba samba-client samba-swat 有依赖关 ...
- python中类的函数中的self
Python类中的self到底是干啥的 Python编写类的时候,每个函数参数第一个参数都是self,一开始我不管它到底是干嘛的,只知道必须要写上.后来对Python渐渐熟悉了一点,再回头看self的 ...
- Zookeeper选举(fastleaderelection算法)
1.选举相关概念: 选票:(myid,zxid,当前节点选取轮次,被推举服务器选举轮次,状态(looking)). 选举发生情况:启动时选举,运行时选举. 外部投票:其他服务器发送来的投票. 内部投票 ...
- [NgRx] NgRx Entity Adapter Configuration - Understanding sortComparer and selectId
import { Course, compareCourses } from "../model/course"; import { EntityState, createEnti ...
- Django+element ui前后端不分离的博客程序
最近把去年写的一个烂尾博客(使用了django1.11和element ui)又重新完善了一下,修改了样式和增加了新功能 github链接:https://github.com/ngauerh/Nag ...