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.

相關文章
相關標籤/搜索