工做中咱們常常利用redis來實現限速, 好比限制一個手機號60秒最多發送3條短信.java
僞代碼以下:redis
long count = incr('手機號') ; spring
if count==1 expire('手機號',60) ; bash
if count>3 return "發送頻率超限" ; 測試
上面代碼在執行時, 客戶端先後可能調用redis兩次, 雖然不影響限速的功能, 但整個操做的原子性可能被破壞掉. lua
咱們能夠利用Lua腳本把incr和expire的調用封裝在一塊兒, 就能夠保證整個操做的原子性. code
定義腳本文件rate_limit.lua,內容以下:ip
-- incr local count = redis.call('incr',KEYS[1]) -- 第一次時,設置expire if count == 1 then redis.call('expire',KEYS[1],ARGV[1]) end -- 返回目前次數 return count
經過redis-cli調用lua測試:it
redis-cli --eval rate_limit.lua 手機號 , 60 (integer) 1 redis-cli --eval rate_limit.lua 手機號 , 60 (integer) 2
若是項目是Java編寫,且使用了spring-data-redis, 可參考代碼以下:class
String lua = ""; lua += "local count = redis.call('incr',KEYS[1]) "; lua += "if count == 1 then "; lua += "redis.call('expire',KEYS[1],ARGV[1]) "; lua += "end "; lua += "return count"; RedisScript redisScript=new DefaultRedisScript(lua,Long.TYPE); Long count=(Long)redisTemplate.execute(redisScript, Arrays.asList("手機號"), 60); if(count>3){ return "發送頻率超限"; }