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

栏目: Lua · 发布时间: 4年前

内容简介:httpc库基于cf框架都内部实现的socket编写的http client库.httpc库内置SSL支持, 在不使用代理的情况下就可以请求第三方接口.httpc支持header、args、body、timeout请求设置, 完美支持各种httpc调用方式.

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
    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.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java Servlet & JSP Cookbook

Java Servlet & JSP Cookbook

Bruce W. Perry / O'Reilly Media / 2003-12-1 / USD 49.99

With literally hundreds of examples and thousands of lines of code, the Java Servlet and JSP Cookbook yields tips and techniques that any Java web developer who uses JavaServer Pages or servlets will ......一起来看看 《Java Servlet & JSP Cookbook》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具