httpc庫基于cf框架都內(nèi)部實(shí)現(xiàn)的socket編寫的http client庫.
httpc庫內(nèi)置SSL支持, 在不使用代理的情況下就可以請求第三方接口.
httpc支持header、args、body、timeout請求設(shè)置, 完美支持各種httpc調(diào)用方式.
httpc庫使用前需要手動導(dǎo)入httpc庫: local httpc = require "httpc"
.
調(diào)用get方法將會對domain發(fā)起一次HTTP GET請求.
domain是一個符合URL定義規(guī)范的字符串;
HEADER是一個key-value數(shù)組, 一般用于添加自定義頭部;
ARGS為請求參數(shù)的key-value數(shù)組, 對于GET方法將會自動格式化為:args[n][1]=args[n][2]&args[n+1][1]=args[n+1][2]
;
TIMEOUT為httpc請求的最大超時時間;
調(diào)用post方法將會對domain發(fā)起一次HTTP POST請求, 此方法的content-type
會被設(shè)置為:application/x-www-form-urlencoded
.
domain是一個符合URL定義規(guī)范的字符串;
HEADER是一個key-value數(shù)組, 一般用于添加自定義頭部; 不支持Content-Type與Content-Length設(shè)置;
BODY是一個key-value數(shù)組, 對于POST方法將會自動格式化為:body[n][1]=body[n][2]&body[n+1][1]=body[n+1][2]
;
TIMEOUT為httpc請求的最大超時時間;
json方法將會對domain發(fā)起一次http POST請求. 此方法的content-type
會被設(shè)置為:application/json
.
HEADER是一個key-value數(shù)組, 一般用于添加自定義頭部; 不支持Content-Type與Content-Length設(shè)置;
JSON必須是一個字符串類型;
TIMEOUT為httpc請求的最大超時時間;
file方法將會對domain發(fā)起一次http POST請求.
HEADER是一個key-value數(shù)組, 一般用于添加自定義頭部; 不支持Content-Type與Content-Length設(shè)置;
FILES是一個key-value數(shù)組, 每個item包含: name(名稱), filename(文件名), file(文件內(nèi)容), type(文件類型)等屬性. 文件類型可選.
TIMEOUT為httpc請求的最大超時時間;
所有httpc請求接口均會有2個返回值: code
, response
. code為http協(xié)議狀態(tài)碼, response為回應(yīng)body(字符串類型).
參數(shù)不正確, 連接被斷開等其它錯誤, code將會為nil, response為錯誤信息.
什么是一次性httpc請求呢?
每次我們使用httpc庫在請求第三方http接口的時候, 都會在接口返回后關(guān)閉連接. 這在日常使用中一邊也沒什么問題.
但是當(dāng)我們需要多次請求同一個接口的時候, 每次請求完畢就關(guān)閉連接顯然不是那么高效, 現(xiàn)在我們嘗試使用一個http class對象來解決這個問題.
注意: httpc class對象不能對不同域名的接口使用同一個連接, 這會返回一個錯誤調(diào)用給使用者.
要使用httpc的class
需要導(dǎo)入httpc的class庫, 導(dǎo)入方式為: local httpc = require "httpc.class"
.
當(dāng)需要使用httpc發(fā)起請求之前, 需要先創(chuàng)建一個httpc的對象, 如: local hc = httpc:new {}
. httpc對象創(chuàng)建與初始化完畢后, 使用方式同上述API所示.
hc
與httpc
擁有相同的API, 但是需要使用不同的調(diào)用方式. 如: hc:get
、hc:post
、hc:json
、hc:file
.
一旦hc
使用完畢時, 需要顯示的調(diào)用hc:close()
方法來關(guān)閉創(chuàng)建的httpc對象并銷毀httpc的連接.
現(xiàn)在, 讓我們將上面學(xué)到的API使用方式運(yùn)用到實(shí)踐中.
在main.lua
中啟動一個httpd
的server.
local httpd = require "httpd"
local json = require "json"
local app = httpd:new("httpd")
app:listen("", 8080)
app:run()
我們先利用httpd
庫啟動一個server服務(wù), 并且對外提供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 = "錯誤的接口調(diào)用方式",
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 = "獲取數(shù)據(jù)失敗",
data = json.null,
})
end
return response
end)
現(xiàn)在代碼已經(jīng)完成! 讓我們打開瀏覽器輸入:http://localhost:8090/ip?ip=8.8.8.8
查看返回數(shù)據(jù).
一個請求對應(yīng)一次回是HTTP協(xié)議的本質(zhì)! 但是我們經(jīng)常會遇到批量請求的業(yè)務(wù)場景, 我們就以此來設(shè)計一個批量請求/返回的例子.
讓我們假設(shè)客戶端將會發(fā)送一次POST請求, body為json類型并且里面包含一個IP數(shù)組: ip_list = {1.1.1.1, 8.8.8.8, 114.114.114.114}.
服務(wù)端在接受到這個數(shù)組之后, 需要將這ip的歸屬地信息一次性返回給客戶端.
app:api('/ips', function(content)
local httpc = require "httpc.class"
if not content.json then
return json.encode({
code = 400,
msg = "錯誤的調(diào)用參數(shù)",
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 = "錯誤的參數(shù)類型",
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無法發(fā)送json, 讓我們使用curl
命令行工具進(jìn)行測試:
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
返回數(shù)據(jù)如下:
{"code":200,"data":[["CLOUDFLARE.COM","CLOUDFLARE.COM","","",""],["GOOGLE.COM","GOOGLE.COM","","","level3.com"],["114DNS.COM","114DNS.COM","","",""]]}
上述例子似乎已經(jīng)非常完美! 我們利用連接保持的方式進(jìn)行了3次請求, 這樣已經(jīng)縮短了請求50%的連接消耗(TCP握手).
但是對于非常需要性能的我們來說: 每次請求需要等到上一個請求處理完畢后才能繼續(xù)發(fā)起新的請求, 這樣的方式顯然還不足以滿足我們.
這樣的情況下, httpc
庫提供了一個叫multi_request
的方法. 具體使用方法在這里.
這個方法可以讓我們同時發(fā)送幾十上百個請求來解決單個連接阻塞的問題.
現(xiàn)在, 讓我使用httpc
庫的multi_request
方法來并發(fā)請求多個接口, 減少連接阻塞帶來的問題.
app:api('/ips_multi', function (content)
local httpc = require "httpc"
if not content.json then
return json.encode({
code = 400,
msg = "錯誤的調(diào)用參數(shù)",
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 = "錯誤的參數(shù)類型",
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)
好的, 現(xiàn)在讓我們再次使用curl
工具進(jìn)行測試:
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的請求回應(yīng)時間看到, 響應(yīng)時間消耗再次降低了50%.
[candy@MacBookPro:~/Documents/core_framework] $ ./cfadmin
[2019/06/16 17:45:21] [INFO] httpd正在監(jiān)聽: 0.0.0.0:8090
[2019/06/16 17:45:21] [INFO] httpd正在運(yùn)行Web Server服務(wù)...
[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 = "錯誤的接口調(diào)用方式",
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 = "獲取數(shù)據(jù)失敗",
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 = "錯誤的調(diào)用參數(shù)",
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 = "錯誤的參數(shù)類型",
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 = "錯誤的調(diào)用參數(shù)",
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 = "錯誤的參數(shù)類型",
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()
下一章節(jié)我們將學(xué)習(xí)如何使用httpd庫編寫Websocket.
更多建議: