vlambda博客
学习文章列表

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 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 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);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..............