使用OpenResty及Redis實現API池,進行動態分配請求及隊列等待及超時處理
為了提供一個完整的實現方案,我們將詳細說明如何使用OpenResty和Redis來構建一個系統,該系統不僅能夠根據QPS限制動態分配請求,還能在超出QPS限制時將額外的請求放入隊列中等待處理,並設置超時機制。
完整實現步驟
1. 環境準備
確保你已經安裝了以下組件:
- OpenResty:Nginx的一個擴展版本,支持Lua腳本。
- Redis:用於存儲API QPS限制、計數以及排隊請求的信息。
- lua-cjson:用於JSON序列化/反序列化。
安裝OpenResty和Redis
# 安裝OpenResty sudo apt-get update sudo apt-get install -y software-properties-common sudo add-apt-repository -y ppa:openresty/ppa sudo apt-get update sudo apt-get install -y openresty # 安裝Redis sudo apt-get install redis-server # 安裝lua-cjson(如果未包含在OpenResty中) sudo luarocks install lua-cjson
2. 配置OpenResty
編輯OpenResty配置文件nginx.conf,通常位於/usr/local/openresty/nginx/conf/nginx.conf或/etc/openresty/nginx.conf。
http {
lua_shared_dict api_limits 10m; # 用於存儲API QPS限制和計數
lua_package_path "/path/to/lua/scripts/?.lua;;"; # 包含自定義Lua腳本路徑
upstream backend_apis {
server api1.example.com;
server api2.example.com;
# 添加更多的API實例
}
server {
listen 80;
location /api/ {
access_by_lua_file /path/to/lua_scripts/api_limit.lua; # 處理QPS限制和排隊邏輯
proxy_pass http://backend_apis;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
}
}
}
3. 編寫Lua腳本
創建目錄/path/to/lua_scripts/並在其中創建兩個Lua腳本文件:api_limit.lua 和 process_queue.lua。
api_limit.lua
local redis = require "resty.redis"
local cjson = require "cjson"
local red = redis:new()
red:set_timeout(1000) -- 1秒超時
-- 連接Redis服務器
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "failed to connect to Redis: ", err)
return ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end
local api_key = ngx.var.uri
local limit = 100 -- 默認QPS限制,可根據實際需要調整
local current_count = tonumber(red:get(api_key)) or 0
if current_count < limit then
red:incr(api_key)
red:expire(api_key, 60) -- 每60秒重置一次計數
else
local queue_name = "queue:" .. api_key
local request_info = {
uri = ngx.var.request_uri,
timestamp = ngx.time() -- 當前時間戳
}
local queued, err = red:rpush(queue_name, cjson.encode(request_info))
if not queued then
ngx.log(ngx.ERR, "failed to push to queue: ", err)
return ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end
return ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS)
end
process_queue.lua
local redis = require "resty.redis"
local cjson = require "cjson"
local function process_queue()
local red = redis:new()
red:set_timeout(1000)
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "failed to connect to Redis: ", err)
return
end
local queue_name = "queue:/api/path" -- 替換為你的API路徑
local timeout_seconds = 30 -- 設置超時時間為30秒
while true do
local request_json, err = red:lpop(queue_name)
if not request_json then
ngx.sleep(1) -- 如果沒有請求在隊列中,稍作休眠
goto continue
end
local request_info = cjson.decode(request_json)
local request_time = request_info.timestamp
local current_time = ngx.time()
if (current_time - request_time) > timeout_seconds then
ngx.log(ngx.WARN, "Request timed out and will be discarded: ", request_info.uri)
goto continue
end
-- 發送請求到後端API
local res = ngx.location.capture("/proxy_backend", { args = { uri = request_info.uri } })
if res.status ~= ngx.HTTP_OK then
ngx.log(ngx.ERR, "failed to process queued request: ", request_info.uri)
end
::continue::
end
end
process_queue()
4. 創建後臺任務腳本
創建一個簡單的Shell腳本或使用cron作業定期運行process_queue.lua腳本。
示例Shell腳本 (run_process_queue.sh)
#!/bin/bash /usr/local/openresty/bin/resty /path/to/lua_scripts/process_queue.lua
賦予執行權限:
chmod +x /path/to/run_process_queue.sh
使用Cron定時執行
編輯crontab以每分鐘運行一次該腳本:
crontab -e
添加如下行:
* * * * * /path/to/run_process_queue.sh
測試與驗證
- 啟動OpenResty:
sudo systemctl start openresty- 測試API限流: 使用curl或其他工具向
/api/your_api_path發送大量請求,觀察是否按照預期行為進行限流並排隊。 - 檢查Redis: 使用
redis-cli命令行工具檢查Redis中的數據結構,確保請求被正確地加入隊列並且超時機制正常工作。
總結
通過上述步驟,我們實現了基於OpenResty和Redis的API限流與排隊系統。該系統能夠在超過QPS限制時將請求排隊,並通過後臺任務異步處理這些請求,同時設置了合理的超時機制以避免長時間等待影響用戶體驗。根據具體需求,你可以進一步優化和擴展這個系統。