Lua Web快速开发指南(7) - 高效的接口调用 - httpc库

httpc库基于cf框架都内部实现的socket编写的http client库.git

httpc库内置SSL支持, 在不使用代理的状况下就能够请求第三方接口.github

httpc支持header、args、body、timeout请求设置, 完美支持各类httpc调用方式.web

API介绍

httpc库使用前须要手动导入httpc库: local httpc = require "httpc".json

httpc.get(domain, HEADER, ARGS, TIMEOUT)

调用get方法将会对domain发起一次HTTP GET请求.c#

domain是一个符合URL定义规范的字符串;api

HEADER是一个key-value数组, 通常用于添加自定义头部;数组

ARGS为请求参数的key-value数组, 对于GET方法将会自动格式化为:args[n][1]=args[n][2]&args[n+1][1]=args[n+1][2];浏览器

TIMEOUT为httpc请求的最大超时时间;bash

httpc.post(domain, HEADER, BODY, TIMEOUT)

调用post方法将会对domain发起一次HTTP POST请求, 此方法的content-type会被设置为:application/x-www-form-urlencoded.并发

domain是一个符合URL定义规范的字符串;

HEADER是一个key-value数组, 通常用于添加自定义头部; 不支持Content-Type与Content-Length设置;

BODY是一个key-value数组, 对于POST方法将会自动格式化为:body[n][1]=body[n][2]&body[n+1][1]=body[n+1][2];

TIMEOUT为httpc请求的最大超时时间;

httpc.json(domain, HEADER, JSON, TIMEOUT)

json方法将会对domain发起一次http POST请求. 此方法的content-type会被设置为:application/json.

HEADER是一个key-value数组, 通常用于添加自定义头部; 不支持Content-Type与Content-Length设置;

JSON必须是一个字符串类型;

TIMEOUT为httpc请求的最大超时时间;

httpc.file(domain, HEADER, FILES, TIMEOUT)

file方法将会对domain发起一次http POST请求.

HEADER是一个key-value数组, 通常用于添加自定义头部; 不支持Content-Type与Content-Length设置;

FILES是一个key-value数组, 每一个item包含: name(名称), filename(文件名), file(文件内容), type(文件类型)等属性. 文件类型可选.

TIMEOUT为httpc请求的最大超时时间;

httpc 返回值

全部httpc请求接口均会有2个返回值: code, response. code为http协议状态码, response为回应body(字符串类型).

参数不正确, 链接被断开等其它错误, code将会为nil, response为错误信息.

"一次性HTTP请求"

什么是一次性httpc请求呢?

每次咱们使用httpc库在请求第三方http接口的时候, 都会在接口返回后关闭链接. 这在平常使用中一边也没什么问题.

可是当咱们须要屡次请求同一个接口的时候, 每次请求完毕就关闭链接显然不是那么高效, 如今咱们尝试使用一个http class对象来解决这个问题.

注意: httpc class对象不能对不一样域名的接口使用同一个链接, 这会返回一个错误调用给使用者.

httpc库的class对象使用介绍

要使用httpc的class须要导入httpc的class库, 导入方式为: local httpc = require "httpc.class".

当须要使用httpc发起请求以前, 须要先建立一个httpc的对象, 如: local hc = httpc:new {}. httpc对象建立与初始化完毕后, 使用方式同上述API所示.

hchttpc拥有相同的API, 可是须要使用不一样的调用方式. 如: hc:gethc:posthc:jsonhc:file.

一旦hc使用完毕时, 须要显示的调用hc:close()方法来关闭建立的httpc对象并销毁httpc的链接.

开始实践

如今, 让咱们将上面学到的API使用方式运用到实践中.

1. 启动一个httpd库的web server

main.lua中启动一个httpd的server.

local httpd = require "httpd"
local json = require "json"

local app = httpd:new("httpd")


app:listen("", 8080)

app:run()
复制代码

1. 增长一个API路由用于ip地址归属地查询

咱们先利用httpd库启动一个server服务, 而且对外提供IP归属地查询接口

app:api('/ip', function(content)
	local httpc = require "httpc"
	local args = content.args
	if not args or not args['ip'] then
		return json.encode({
			code = 400,
			msg = "错误的接口调用方式",
			data = json.null,
			})
	end
	local code, response = httpc.get("http://freeapi.ipip.net/"..args["ip"])
	if code ~= 200 then
		return json.encode({
			code = 401,
			msg = "获取数据失败",
			data = json.null,
			})
	end
	return response
end)
复制代码

如今代码已经完成! 让咱们打开浏览器输入:http://localhost:8090/ip?ip=8.8.8.8查看返回数据.

2. 查询多个IP地址的归属地

一个请求对应一次回是HTTP协议的本质! 可是咱们常常会遇到批量请求的业务场景, 咱们就以此来设计一个批量请求/返回的例子.

让咱们假设客户端将会发送一次POST请求, body为json类型而且里面包含一个IP数组: ip_list = {1.1.1.1, 8.8.8.8, 114.114.114.114}.

服务端在接受到这个数组以后, 须要将这ip的归属地信息一次性返回给客户端.

app:api('/ips', function(content)
	local httpc = require "httpc.class"
	if not content.json then
		return json.encode({
			code = 400,
			msg  = "错误的调用参数",
			data = json.null,
		})
	end
	local args = json.decode(content.body)
	if type(args) ~= 'table' or type(args['ip_list']) ~= 'table' then
		return json.encode({
			code = 400,
			msg  = "错误的参数类型",
			data = json.null,
		})
	end
	local hc = httpc:new {}
	local ret = { code = 200 , data = {}}
	for _, ip in ipairs(args['ip_list']) do
		local code, response = hc:get("http://freeapi.ipip.net/"..ip)
		ret['data'][#ret['data']+1] = json.decode(response)
	end
	return json.encode(ret)
end)
复制代码

因为普通浏览器POST没法发送json, 让咱们使用curl命令行工具进行测试:

curl -H "Content-Type: application/json" -X POST -d '{"ip_list":["1.1.1.1","8.8.8.8","114.114.114.114"]}' http://localhost:8090/ip
复制代码

返回数据以下:

{"code":200,"data":[["CLOUDFLARE.COM","CLOUDFLARE.COM","","",""],["GOOGLE.COM","GOOGLE.COM","","","level3.com"],["114DNS.COM","114DNS.COM","","",""]]}
复制代码

3. 持续优化.

上述例子彷佛已经很是完美! 咱们利用链接保持的方式进行了3次请求, 这样已经缩短了请求50%的链接消耗(TCP握手).

可是对于很是须要性能的咱们来讲: 每次请求须要等到上一个请求处理完毕后才能继续发起新的请求, 这样的方式显然还不足以知足咱们.

这样的状况下, httpc库提供了一个叫multi_request的方法. 具体使用方法在这里.

这个方法可让咱们同时发送几十上百个请求来解决单个链接阻塞的问题.

4. 并发请求

如今, 让我使用httpc库的multi_request方法来并发请求多个接口, 减小链接阻塞带来的问题.

app:api('/ips_multi', function (content)
	local httpc = require "httpc"
	if not content.json then
		return json.encode({
			code = 400,
			msg  = "错误的调用参数",
			data = json.null,
		})
	end
	local args = json.decode(content.body)
	if type(args) ~= 'table' or type(args['ip_list']) ~= 'table' then
		return json.encode({
			code = 400,
			msg  = "错误的参数类型",
			data = json.null,
		})
	end
	local requests = {}
	local responses = { code = 200, data = {}}
	for _, ip in ipairs(args["ip_list"]) do
		requests[#requests+1] = {
			domain = "http://freeapi.ipip.net/"..ip,
			method = "get",
		}
	end
	local ok, ret = httpc.multi_request(requests)
	for _, res in ipairs(ret) do
		responses['data'][#responses['data'] + 1] = res
	end
	return json.encode(responses)
end)
复制代码

好的, 如今让咱们再次使用curl工具进行测试:

curl -H "Content-Type: application/json" -X POST -d '{"ip_list":["1.1.1.1","8.8.8.8","114.114.114.114"]}' http://localhost:8090/ips_multi
复制代码

咱们能够从cf的请求回应时间看到, 响应时间消耗再次下降了50%.

[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
[2019/06/16 17:45:21] [INFO] httpd正在监听: 0.0.0.0:8090
[2019/06/16 17:45:21] [INFO] httpd正在运行Web Server服务...
[2019/06/16 17:45:23] - ::1 - ::1 - /ips_multi - POST - 200 - req_time: 0.140253/Sec
[2019/06/16 17:45:38] - ::1 - ::1 - /ips - POST - 200 - req_time: 0.288286/Sec
复制代码

完整的代码

local httpd = require "httpd"
local json = require "json"

local app = httpd:new("httpd")

app:api('/ip', function(content)
	local httpc = require "httpc"
	local args = content.args
	if not args or not args['ip'] then
		return json.encode({
			code = 400,
			msg = "错误的接口调用方式",
			data = json.null,
			})
	end
	local code, response = httpc.get("http://freeapi.ipip.net/"..args["ip"])
	if code ~= 200 then
		return json.encode({
			code = 401,
			msg = "获取数据失败",
			data = json.null,
			})
	end
	return response
end)

app:api('/ips', function(content)
	local httpc = require "httpc.class"
	if not content.json then
		return json.encode({
			code = 400,
			msg  = "错误的调用参数",
			data = json.null,
		})
	end
	local args = json.decode(content.body)
	if type(args) ~= 'table' or type(args['ip_list']) ~= 'table' then
		return json.encode({
			code = 400,
			msg  = "错误的参数类型",
			data = json.null,
		})
	end
	local hc = httpc:new {}
	local ret = { code = 200 , data = {}}
	for _, ip in ipairs(args['ip_list']) do
		local code, response = hc:get("http://freeapi.ipip.net/"..ip)
		ret['data'][#ret['data']+1] = json.decode(response)
	end
	return json.encode(ret)
end)

app:api('/ips_multi', function (content)
	local httpc = require "httpc"
	if not content.json then
		return json.encode({
			code = 400,
			msg  = "错误的调用参数",
			data = json.null,
		})
	end
	local args = json.decode(content.body)
	if type(args) ~= 'table' or type(args['ip_list']) ~= 'table' then
		return json.encode({
			code = 400,
			msg  = "错误的参数类型",
			data = json.null,
		})
	end
	local requests = {}
	local responses = { code = 200, data = {}}
	for _, ip in ipairs(args["ip_list"]) do
		requests[#requests+1] = {
			domain = "http://freeapi.ipip.net/"..ip,
			method = "get",
		}
	end
	local ok, ret = httpc.multi_request(requests)
	for _, res in ipairs(ret) do
		responses['data'][#responses['data'] + 1] = res
	end
	return json.encode(responses)
end)

app:listen("", 8090)

app:run()
复制代码

继续学习

下一章节咱们将学习如何使用httpd库编写Websocket.

相关文章
相关标签/搜索