系列文章索引

OpenResty使用Lua大全(一)Lua语法入门实战
OpenResty使用Lua大全(二)在OpenResty中使用Lua
OpenResty使用Lua大全(三)OpenResty使用Json模块解析json
OpenResty使用Lua大全(四)OpenResty中使用Redis
OpenResty使用Lua大全(五)OpenResty中使用MySQL
OpenResty使用Lua大全(六)OpenResty发送http请求
OpenResty使用Lua大全(七)OpenResty使用全局缓存
OpenResty使用Lua大全(八)OpenResty执行流程与阶段详解
OpenResty使用Lua大全(九)实战:nginx-lua-redis实现访问频率控制
OpenResty使用Lua大全(十)实战: Lua + Redis 实现动态封禁 IP
OpenResty使用Lua大全(十一)实战: nginx实现接口签名安全认证
OpenResty使用Lua大全(十二)实战: 动手实现一个网关框架

一、发起http请求

1、发起内部请求

(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
提高性能的方式: 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)
    }
}

2、发起外部请求

因为ngx.location.capture不能直接发起外部请求,我们需要通过内部请求中用反向代理请求发起外部请求

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type text/html;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        charset utf-8;
        lua_code_cache off;

		location /product {
		        internal;
		    proxy_pass "https://s.taobao.com/search?q=iphone";
		}
		
		location /order {
		        content_by_lua_block {
		        local res = ngx.location.capture("/product");
		        ngx.say(res.status)
		        ngx.say(res.body)
		     }
		}

        location / {
            root   html;
            index  index.html index.htm;
        }
    }
}


3、动态变量

刚才我们请求外部请求,是写死了q=iphone,那我们用capture传参

location /product {
	internal;
	resolver 8.8.8.8;
	proxy_set_header Accept-Encoding ' ';
	proxy_pass "https://s.taobao.com/search?q=$arg_q";
}
        
location /order {
	content_by_lua_block {
		local get_args = ngx.req.get_uri_args();
		local res = ngx.location.capture("/product",{
			method = ngx.HTTP_GET,
			args = {q=get_args["q"]}
		});
		ngx.say(res.status)
		ngx.say(res.body)
	}
}

注意:在proxy_pass 中使用变量,需要使用resolver指令解析变量中的域名

google 域名解析:
resolver 8.8.8.8;

这样我们请求传q参数的值,随便由用户决定查询什么值。
我们这边就发现了请求外部服务的时候发现比较复杂,我们可以借用第三方的库 resty.http
从可实现外部请求,而且使用很方便。

二、使用resty.http模块

1、下载安装

OpenResty默认没有提供Http客户端,需要使用第三方提供
我们可以从github上搜索相应的客户端,比如https://github.com/pintsized/lua-resty-http 该网址上也有教程!

只要将 lua-resty-http/lib/resty/ 目录下的 http.luahttp_connect.luahttp_headers.lua
两个文件拷贝到 /usr/local/openresty/lualib/resty 目录下即可(假设你的 OpenResty 安装目录为 /usr/local/openresty)

cd /usr/local/openresty/lualib/resty
wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_headers.lua
wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http.lua
wget https://raw.githubusercontent.com/pintsized/lua-resty-http/master/lib/resty/http_connect.lua

2、使用

local res, err = httpc:request_uri(uri, {  
    method = "POST/GET",  ---请求方式
    query = str,  ---get方式传参数
    body = str,	 ---post方式传参数
    path = "url" ----路径
    headers = {  ---header参数
        ["Content-Type"] = "application/json",  
    }  
})  
--引入http模块
local http = require("resty.http")
--创建http客户端实例
local httpc = http.new()
--request_uri函数请求淘宝
local resp, err = httpc:request_uri("https://s.taobao.com", {
    method = "GET",    		---请求方式
    query = "q=iphone&b=2",   ---get方式传参数
    body = "c=3&d=4",  		---post方式传参数
    path = "/search", 		----路径
    headers = { 			---header参数
        ["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) ",
        ["token"] = "1234456"
    }
})

if not resp then
    ngx.say("request error :", err)
    return
end

--获取返回的状态码
ngx.status = resp.status

if ngx.status ~= 200 then  
    ngx.log(ngx.WARN,"非200状态,ngx.status:"..ngx.status)  
    return resStr  
end 

--获取遍历返回的头信息
for k, v in pairs(resp.headers) do
    if type(v) == "table" then  
        ngx.log(ngx.WARN,"table:"..k, ": ", table.concat(v, ", "))  
    else  
        ngx.log(ngx.WARN,"one:"..k, ": ", v)  
    end  
end

--响应体
ngx.say(resp.body)

httpc:close()

如果发现报错
request error :no resolver defined to resolve “xxxx”

此错误是因为要配置DNS解析器resolver 8.8.8.8,否则域名是无法解析的。
在nginx.conf配置文件中 http模块加上resolver 8.8.8.8; Google提供的免费DNS服务器的IP地址
配置好后,重启nginx

访问https错误,因为我们访问的https,需要配置ssl证书
在nginx配置文件中,server虚拟主机模块设置

lua_ssl_verify_depth 2;
lua_ssl_trusted_certificate "/etc/ssl/certs/ca-bundle.crt";

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐