服務限流demo(lua指令碼)
本文提供了兩種服務限流的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!")