redis
實現限速器的幾種方式。html
先獲取 key
的當前值,若是沒有超出限制再執行 INCR
增1,若是 key
不存在,使用 redis
的事務初始化 key
和過時時間。redis
僞代碼:併發
count = redis.GET(key) if redis return nil { redis.MULTI redis.INCR(key) redis.EXPIRE(key, expire_time) redis.EXEC count = 1 } if count > limit { return 超出限制 } else { redis.INCR(key) }
高併發下的問題:高併發
若是同時10個併發程序執行 GET
返回了 nil
, 那麼這10個併發程序都會執行 redis
的事務將 key
增一,但每一個程序的 count
值都爲1,若是 limit
設置的值小於10,那麼真正執行的程序就超過限制了。若是執行完事務後再查一次 redis
賦值給 count
,那麼每一個程序可能都會返回10,從而沒有程序可以繼續執行。lua
key
已經存在的狀況下,先 GET
後 INCR
的邏輯也可能會出現實際執行的程序數多於 limit
的狀況。code
先 INCR
, 若是值爲1說明是 key
剛設置的,此時再執行 EXPIRE
htm
僞代碼:事務
count = redis.INCR(key) if count == 1 { redis.EXPIRE(key, expire_time) } if count > limit { return 超出限制 }
慎用get
若是 INCR
以後程序掛掉了,沒有執行 EXPIRE
, 那麼這個 key
就沒有過時時間了,具體的影響視需求而定。string
摘自 http://redisdoc.com/string/incr.html, 我不會lua
local current current = redis.call("incr",KEYS[1]) if tonumber(current) == 1 then redis.call("expire",KEYS[1],1) end