nginx结合lua实现策略性限流量控制
简介
依据
预定义的参数
或者初始化配置文件的参数
,获取请求参数
与之对应,实行策 略性
限制流量达到可控
的目的。使用openresty的lua-resty-limit-traffic模块进行限流。
准备工作
需要使用到以下两个模块可以移步到此文章查看详细介绍(
字符串分割strsplit模块
、nginx 共享缓存get以及set的ngxshare模块
)
nginx.conf 配置
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
# 设置纯 Lua 扩展库的搜寻路径(';;' 是默认路径)
lua_package_path "/data/www/code/nginx+lua/config/lua_p/?.lua;;";
# 设置 C 编写的 Lua 扩展模块的搜寻路径(也可以用 ';;')
lua_package_cpath "/data/www/code/nginx+lua/config/lua_p_c/?.so;;";
#共享缓存
lua_shared_dict my_limit_store 5m;
lua_shared_dict my_limit_req_store 5m;
#初始化限流
init_by_lua_file config/lua/limit_init.lua;
server {
listen 8080;
location /limiter_reset {
lua_code_cache off;
content_by_lua_file ./config/lua/limit_reset.lua;
charset utf-8;
}
location /limiter {
lua_code_cache off;
content_by_lua_file ./config/lua/limiter.lua;
charset utf-8;
}
}
}
limit_init.lua(初始化lua脚本内容
)
local limit_table = ngx.shared.my_limit_store;
local strsplit = require("strsplit");
file = io.open("config/init/limit.txt", "r");
if nil == file then
ngx.log(ngx.INFO, "文件读取失败,将采用默认值进行赋值");
local suc, err = limit_table:set("default", "15-15");
else
for line in file:lines() do
local splitTable = strsplit.split(line, "-");
local sysFlag = splitTable[1];
local limitRate = splitTable[2];
local bursts = splitTable[3];
local tableVal = string.format("%s-%s", limitRate, bursts);
ngx.log(ngx.INFO, "sysFlag = ", sysFlag, "限流阀值:流速-桶容量:", tableVal);
limit_table:set(sysFlag, tableVal);
end
file:close();
end
limit_reset.lua文件(依据GET请求参数重置策略值)
local strsplit = require("strsplit");
local ngxshare = require("resty.ngxshare");
-- get request param
local args, err = ngx.req.get_uri_args();
local limitvalue = args["limitvalue"];
ngx.say("-----------limitvalue-----------", limitvalue);
local limitParam = strsplit.split(limitvalue, "-");
local sysFlag = limitParam[1];
ngx.say(ngxshare.shared_dic_get(ngx.shared.my_limit_store, sysFlag));
local rateBurst = string.format("%s-%s", limitParam[2], limitParam[3]);
ngx.say("-------------rateBurst:", rateBurst);
ngxshare.shared_dic_set(ngx.shared.my_limit_store, sysFlag, rateBurst, 0);
ngx.say("---------------", ngxshare.shared_dic_get(ngx.shared.my_limit_store, sysFlag));
limit.txt文件 (限流策略配置文件
)
test-10-200 # 其中test为请求渠道标识,10 为流速, 200为桶容量
test1-1-1
limit.lua文件 (策略性限流主文件
)
-- 获取请求参数
local strsplit = require("strsplit");
ngx.req.read_body();
local args, err = ngx.req.get_uri_args();
local sysFlag = args["sys"];
local limit_table = ngx.shared.my_limit_store;
local limitRate = limit_table:get(sysFlag);
if not limitRate then
ngx.log(ngx.INFO, "sysFlag can not found so set defalut value");
limitRate = limit_table:get("default");
end
-- 获取到的值进行拆分并限流
local limitValue = strsplit.split(limitRate, "-");
local rate = tonumber(limitValue[1]);
local burst = tonumber(limitValue[2]);
local limit_req = require "resty.limit.req";
ngx.say("rate====", rate, "---burst=====", burst);
-- 根据配置项创建一个限流的table。
local lim, err = limit_req.new("my_limit_req_store", rate, burst);
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(sysFlag, true);
if not delay then
if err == "rejected" then
ngx.say("rejected access service..............");
ngx.log(ngx.INFO, "rejected access service..............", err);
return ngx.exit(503);
end
ngx.log(ngx.INFO, "failed to limit req: ", err);
return ngx.exit(500);
end
ngx.log(ngx.INFO, "access received..............");
ngx.say("access received..............");
测试
# 测试一:http://192.168.56.2:8080/limiter?sys=t
#未定义的策略性标识给其默认值
rate====15---burst=====15
access received..............
# 测试二:http://192.168.56.2:8080/limiter?sys=test
#定义的策略性标识解析
rate====10---burst=====200
access received..............
# 测试三:http://192.168.56.2:8080/limiter?sys=test1
#一秒内请求第一次通过
rate====1---burst=====1
access received..............
#一秒内请求第二次被限制
rate====1---burst=====1
rejected access service..............