本文提供了两种服务限流的demo,以开拓思路,仅供参考。
1. 基于漏统原理
local function close_redis(red)
if not red then
return
end
local ok, err = red:close()
if not ok then
ngx.say("close redis error : ", err)
end
end
local function lua_string_split(str)
local sub_str_tab = {};
local local_server_name;
for mu_id in string.gmatch(str, "(%w*)/") do
table.insert(sub_str_tab, mu_id)
end
if #sub_str_tab >= 2 then
local_server_name = sub_str_tab[2]
else
local_server_name = nil
end
return local_server_name;
end
local redis = require("resty.redis")
--创建实例
local red = redis:new()
--设置超时(毫秒)
red:set_timeout(1000)
--建立连接
local ip = "127.0.0.1"
local port = 6379
local ok, err = red:connect(ip, port)
if not ok then
ngx.say("connect to redis error : ", err)
return close_redis(red)
end
-- 获取当前请求的uri
local uri = ngx.var.uri
--解析服务名称
local server_name = lua_string_split(uri)
--define time_distance to check unit:s
local time_distance = 60
--assume rate value red redis and config by ui unit: r/s
--this rate get by server_name
--code sample: server_rate = red:get(server_name)
local server_rate = 2
--读取远程地址,限制的对象或者从request里获取
local remoteAdd = ngx.var.remote_addr
local key = server_name..remoteAdd
--请求量
local server_reqs = server_rate * time_distance
--set key expire
local ok,err = red:expire(key, time_distance)
if not ok then
ngx.log(ngx.WARN, "redis set expire error: ", err)
close_redis(red)
return nil
end
--push list value
local ok, err = red:rpush(key, ngx.time())
if not ok then
ngx.log(ngx.WARN, "redis rpush error: ", err)
close_redis(red)
return nil
end
--lrange key list value
local res, err = red:lrange(key, -server_reqs, -1)
if not ok then
ngx.log(ngx.WARN, "redis lrange error: ", err)
close_redis(red)
return nil
end
--finally
close_redis(red)
if #res >= server_reqs or res[#res] - res[1] >= time_distance then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
ngx.say(server_name.." status is ok!")
2. 基于计数器
local function close_redis(red)
if not red then
return
end
local ok, err = red:close()
if not ok then
ngx.say("close redis error : ", err)
end
end
local function wait()
ngx.sleep(1)
end
local function lua_string_split(str)
local sub_str_tab = {};
local local_server_name;
for mu_id in string.gmatch(str, "(%w*)/") do
table.insert(sub_str_tab, mu_id)
end
if #sub_str_tab >= 2 then
local_server_name = sub_str_tab[2]
else
local_server_name = nil
end
return local_server_name;
end
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = "127.0.0.1"
local port = 6379
local ok, err = red:connect(ip,port)
if not ok then
return close_redis(red)
end
-- 获取当前请求的uri
local uri = ngx.var.uri
--解析服务名称
local server_name = lua_string_split(uri)
--define time_distance to check unit:s
local time_distance = 60
--assume rate value red redis and config by ui unit: r/s
--this rate get by server_name
--code sample: server_rate = red:get(server_name)
local server_rate = 100
--读取远程地址,限制的对象或者从request里获取
local remoteAdd = ngx.var.remote_addr
local key = server_name..remoteAdd
--请求量
local server_reqs = server_rate * time_distance
local 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,key,time_distance)
if not ok then
ngx.log(ngx.WARN, "redis lrange error: ", err)
close_redis(red)
return nil
end
--finally
close_redis(red)
--判断总请求量
if res >= server_reqs then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
ngx.say(server_name.." status is ok!")