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 thenngx.log(ngx.INFO, "文件读取失败,将采用默认值进行赋值");local suc, err = limit_table:set("default", "15-15");elsefor line in file:lines() dolocal 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);endfile:close();endlimit_reset.lua文件(依据GET请求参数重置策略值)local strsplit = require("strsplit");local ngxshare = require("resty.ngxshare");-- get request paramlocal 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 thenngx.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 thenngx.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 thenif err == "rejected" thenngx.say("rejected access service..............");ngx.log(ngx.INFO, "rejected access service..............", err);return ngx.exit(503);endngx.log(ngx.INFO, "failed to limit req: ", err);return ngx.exit(500);endngx.log(ngx.INFO, "access received..............");ngx.say("access received..............");
测试
# 测试一:http://192.168.56.2:8080/limiter?sys=t#未定义的策略性标识给其默认值rate====15---burst=====15access received..............# 测试二:http://192.168.56.2:8080/limiter?sys=test#定义的策略性标识解析rate====10---burst=====200access received..............# 测试三:http://192.168.56.2:8080/limiter?sys=test1#一秒内请求第一次通过rate====1---burst=====1access received..............#一秒内请求第二次被限制rate====1---burst=====1rejected access service..............
