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获取目标数据(限流的指标)
- 获取不到不能影响其他阶段继续执行
-
确定限流方式
- 限请求速率
- 限连接数