lua限流腳本:java
local key_local = redis.call('setnx',KEYS[1],0) if tonumber(key_local) == 0 then if tonumber(redis.call('get',KEYS[1]))>=tonumber(ARGV[2]) then return false else redis.call('incr',KEYS[1]) return true end else redis.call('incr',KEYS[1]) redis.call('pexpire',KEYS[1],ARGV[1]) return true end
java調用代碼:redis
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.scripting.support.ResourceScriptSource; import org.springframework.stereotype.Component; import java.util.Collections; @Component public class GlobalLimitComponent { @Autowired private StringRedisTemplate redisTemplate; /** * 針對某個key使用lua腳本進行限流 * 使用lua優勢,能夠保證多個命令是一次行傳輸到Redis服務器而且是串行執行的,保證串行執行的命令中不行插入其餘命令,防止併發問題 * 步驟: * 一、判斷key是否存在,若是不存在,保存該key,設置值爲1,設置多少毫秒(n)最多進行幾回(m) * 二、若是值存在,先判斷key是否超時了,若是超時則刪除,並從新執行步驟1,若是key未超時,則判斷是否超過n毫秒最多m次的限制 * * @param key */ public Boolean getGlobalLimitByLua(String key, int mlitimes, int maxCount) { DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>(); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("/limit.lua"))); redisScript.setResultType(Boolean.class); return redisTemplate.execute(redisScript, Collections.singletonList(key), String.valueOf(mlitimes), String.valueOf(maxCount)); } }
優化點:lua腳本不用每次都上傳,能夠上傳到redis服務器後得到hash值,每次調用hash值進行執行;spring