文档章节

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

水果糖的小铺子
 水果糖的小铺子
发布于 06/16 17:58
字数 2194
阅读 16
收藏 0
Lua

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

httpc库内置SSL支持, 在不使用代理的情况下就可以请求第三方接口.

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

API介绍

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

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

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

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

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

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

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

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
	hc:close()
	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
	hc:close()
	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.

© 著作权归作者所有

水果糖的小铺子
粉丝 23
博文 153
码字总数 73315
作品 1
广州
程序员
私信 提问
Lua Web快速开发指南(1) - 初识cf框架

cf是什么? cf全称为: CoreFramework. 一个基于Reactor事件驱动与协程的lua高性能网络框架, 目前主要面向HTTP Application开发. cf内部主要实现了包括HTTP与HTTP Over Websoket协议的Server,...

水果糖的小铺子
06/14
0
0
cf v0.3 正式版发布, 一套完善的 lua Web 开发框架.

本次更新内容: new feature: 增加httpc的类封装, 用于socket复用场景、多接口并发请求支持; 新增3rd目录支持用户自动集成第三方库; 新增异步stdout日志打印方法, 定期刷新cache提升性能; 新增...

水果糖的小铺子
06/14
0
1
core_framework 0.4 发布,提供 windows 平台支持

更新日志 修复的字段错误的问题. 修复在平台的莫名其妙段错误. 修复了默认不使用的问题. 优化函数的实现方法, 使用更加简单的函数实现. 优化了对各种后端选择的优先级判断. 优化一大部分 C ...

水果糖的小铺子
07/15
0
0
轻量级 lua 网络开发框架 - core_framework

core_framework 是一个基于 libev 的轻量级 lua 网络开发框架。 cf使用lua脚本语言进行服务端业务逻辑开发, 在其内部实现了多种网络协议与第三方库用来帮助使用者进行项目原型的快速开发. c...

水果糖
03/27
0
0
cf v0.3-RC2 发布,admin 功能增加与 bug 修复

更新一览: bug fix: 1. 修复websocket断开连接后导致的循环引用问题; 2. 修复admin before在特殊情况下Token错误判断导致401的问题; 3. 修复定时器的停止判断的问题; 4. 修复DB与Cache的初始...

水果糖
05/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

BlockLang 0.3.0 发布啦

BlockLang 官网:https://blocklang.com Block Lang 0.3.0 引入一个新概念:组件市场。 拼装软件时使用的零部件统称为组件。所有组件在组件市场中统一管理,包括 Block Lang 的内置组件。 组...

blocklang
今天
2
0
OSChina 周日乱弹 —— 请务必让我分担他们的痛苦!

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @clouddyy :分享石元丈晴的单曲《Why》: 《Why》- 石元丈晴 手机党少年们想听歌,请使劲儿戳(这里) @一代码哥 :当他妈狗屁的程序员,天天...

小小编辑
今天
593
7
php 遇到 No input file specified的解决方法

(一)IIS Noinput file specified 方法一:改PHP.ini中的doc_root行,打开ini文件注释掉此行,然后重启IIS 方法二: 请修改php.ini 找到 ; cgi.force_redirect = 1 去掉前面分号,把后面的1...

chenhongjiang
今天
11
0
MySQL 基础

一、常用命令 在命令行中,配置好环境变量后,通过cmd可以直接进入mysql命令行模式,同时列举几种常用命令 # 进入mysql数据库,密码可以先不写,打完-p后再输入,防止被别人看到mysql -u账...

华山猛男
今天
6
0
简单的博客系统(四)Django请求HTML页面视图信息--基于函数的视图

1. 编写用于查询数据的功能函数 应用目录 下的 views.py 文件通常用于保存响应各种请求的函数或类 from django.shortcuts import renderfrom .models import BlogArticles# Create your ...

ZeroBit
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部