ngx-lua實現高級限流方式一
基於POST請求體中的某個參數限流
背景
電商平台有活動,活動涉及優惠券的搶券,優惠券系統對大並發支援略差,為了保護整體系統平穩,因此在入口Nginx層對搶券介面做了一層限流。
完整實現如下:
lua_shared_dict my_limit_req_store 100m;
server {
listen 80;
server_name test.abc.com;
# 搶券介面
location = /api/v1/test {
lua_need_request_body on;
access_by_lua_block {
local cjson = require("cjson")
-- 獲取POST請求體
if ngx.var.request_method == "POST" then
ngx.req.read_body()
local data = ngx.req.get_body_data()
-- 獲取限流欄位 appName, 獲取不到則跳出lua
if data then
params = cjson.decode(data)
if params["appName"] then
limit_key = params["appName"]
else
ngx.log(ngx.ERR, "未獲取到appName,不做限流")
-- 退出access_by_lua階段,繼續執行其他階段。
ngx.exit(0)
end
else
ngx.log(ngx.ERR, "獲取請求體失敗,跳過限流配置")
ngx.exit(0)
end
else
ngx.log(ngx.ERR, "不對非POST請求進行處理")
ngx.exit(0)
end
-- 限流邏輯
local limit_req = require "resty.limit.req"
local lim, err = limit_req.new("my_limit_req_store", 200, 100)
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
local delay, err = lim:incoming(limit_key, true)
if not delay then
if err == "rejected" then
return ngx.exit(503)
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return ngx.exit(500)
end
if delay >= 0.001 then
local excess = err
ngx.sleep(delay)
end
}
try_files $uri $uri/ /index.php?$query_string;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
# 設置項目根目錄
set $PROJECT_NAME "/data/www/test.abc.com/public";
# 設置upstream_name
set $fastcgi_name test;
index index.php index.html index.htm;
include fastcgi_params;
fastcgi_pass $fastcgi_name;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $PROJECT_NAME$fastcgi_script_name; # 指定項目根目錄
}
}
核心解讀
-
獲取請求體
- POST請求
- JSON格式
- 獲取不到不能影響其他階段繼續執行
-
解析請求體獲取目標參數
- cjson.decode()
- 操作map獲取目標數據(限流的指標)
- 獲取不到不能影響其他階段繼續執行
-
確定限流方式
- 限請求速率
- 限連接數