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