nginx lua redis 实现简单的防刷与限流

  运维

防刷

-- access_by_lua_file '/opt/ops/lua/access_limit.lua'
local function close_redis(red)
    if not red then
        return
    end
    --释放连接(连接池实现)
    local pool_max_idle_time = 10000 --毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
 
    if not ok then
        ngx_log(ngx_ERR, "set redis keepalive error : ", err)
    end
end
 
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = "172.16.0.2"
local port = 6379
local ok, err = red:connect(ip,port)
if not ok then
    return close_redis(red)
end
 
local clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then
   clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
   clientIP = ngx.var.remote_addr
end
 
local incrKey = "user:"..clientIP..":freq"
local blockKey = "user:"..clientIP..":block"
 
local is_block,err = red:get(blockKey) -- check if ip is blocked
if tonumber(is_block) == 1 then
   ngx.exit(ngx.HTTP_FORBIDDEN)
   return close_redis(red)
end
 
res, err = red:incr(incrKey)
 
if res == 1 then
   res, err = red:expire(incrKey,1)
end
 
 --1秒200次视为非法,阻止访问600秒
if res > 200 then
    res, err = red:set(blockKey,1)
    res, err = red:expire(blockKey,600)
end
 
close_redis(red)

限流,大于额定QPS后开始排队(还一种方案是检测CPU使用率,当达到阀值时开启限流)

-- access_by_lua_file '/opt/ops/lua/access_flow_control.lua'
local function close_redis(red)
    if not red then
        return
    end
    --释放连接(连接池实现)
    local pool_max_idle_time = 10000 --毫秒
    local pool_size = 100 --连接池大小
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
 
    if not ok then
        ngx_log(ngx_ERR, "set redis keepalive error : ", err)
    end
end
 
local function wait()
   ngx.sleep(1)
end
 
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = "172.16.0.2"
local port = 6379
local ok, err = red:connect(ip,port)
if not ok then
    return close_redis(red)
end
 
local uri = ngx.var.uri -- 获取当前请求的uri
local uriKey = "req:uri:"..uri
res, err = red:eval("local res, err = redis.call('incr',KEYS[1]) if res == 1 then local resexpire, err = redis.call('expire',KEYS[1],KEYS[2]) end return (res)",2,uriKey,1)
while (res > 10)
do 
   local twait, err = ngx.thread.spawn(wait)
   ok, threadres = ngx.thread.wait(twait)
   if not ok then
      ngx_log(ngx_ERR, "wait sleep error: ", err)
      break;
   end
   res, err = red:eval("local res, err = redis.call('incr',KEYS[1]) if res == 1 then local resexpire, err = redis.call('expire',KEYS[1],KEYS[2]) end return (res)",2,uriKey,1)
end
close_redis(red)

LEAVE A COMMENT

发表评论前,请选对水果【Apple=苹果、Banana=香蕉、Pineapple=菠萝、Grapes=葡萄】 Banana...