nginx-ingress + redis 限流
阿新 • • 發佈:2020-11-12
簡介
下面的程式碼會在redis裡面儲存固定長度一個佇列
每次請求向插入一個時間值
比較最新值和最老值之間的時間差(單位是秒)
如果時間差小於某個值說明請求過快
redis.lua準備
檢視一下nginx-ingress裡面是否存在redis.lua, 預設檔案位置: /usr/local/lib/lua/resty/redis.lua
如果沒有需要掛載一個redis.lua
redis.lua可以從openresty專案裡拿, 連結地址: https://github.com/openresty/lua-resty-redis/blob/master/lib/resty/redis.lua
ingress配置
nginx.ingress.kubernetes.io/server-snippet: | access_by_lua_block { local header = ngx.req.get_headers() if header.token then local LIMIT = 100 local DELAY = 10 local red = require "resty.redis" local redis = red:new() redis:set_timeout(1000) local ok, err = redis:connect("redis", 6679) if not ok then ngx.status = 500 ngx.say("<h1>系統開小差了</h1>") return end local res, err = redis:auth("123456") if not res then ngx.status = 500 ngx.say("<h1>系統開小差了</h1>") return end local now = ngx.now() local ok, err = redis:eval('local oldest = redis.call("lindex", ARGV[1], -1);if oldest then if redis.call("llen", ARGV[1]) >= tonumber(KEYS[1]) then if (ARGV[2] - oldest) < tonumber(KEYS[2]) then return nil end end end;redis.call("lpush", ARGV[1], ARGV[2]);redis.call("expire", ARGV[1], KEYS[1]);redis.call("ltrim", ARGV[1], 0, KEYS[1]); return 1', 2, LIMIT-1, DELAY, "limit:"..ngx.md5(header.token), now) if ok ~= 1 then ngx.status = 519 ngx.say("<h1>系統繁忙</h1>") redis:set_keepalive(10000, 100) return end redis:set_keepalive(10000, 100) end }
程式碼解釋
LIMIT 是限制請求的數量, redis儲存的佇列長度等於LIMIT, 佇列長度過長會佔用redis的記憶體
DELAY 表示在多少秒內接受LIMIT個請求
redis:eval 執行了一段lua程式碼
KEYS[1] KEYS[2] 分別表示 LIMIT 和 DELAY
ARGV[1] 表示限速的key, 上面程式碼中是取的 header中的token, 加上 "limit:" 字首當作redis的key
ARGV[2] 是當前的時間, 比如: 1604731480.085
lindex 取出佇列最早的元素oldest(其實就是佇列中最早的時間), 拿當前時間-oldest 如果小於 DELAY 說明訪問過快
如果沒有達到限速閾值, 使用 lpush 把當前時間放到佇列中, 然後使用 ltrim 把佇列裁剪成LIMIT長度, 其實就是去除了最老的值