lua 限流脚本:
- 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 调用代码:
- 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 服务器并且是串行执行的, 保证串行执行的命令中不行插入其他命令, 防止并发问题
- * 步骤:
- * 1, 判断 key 是否存在, 如果不存在, 保存该 key, 设置值为 1, 设置多少毫秒 (n) 最多进行几次(m)
- * 2, 如果值存在, 先判断 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 值进行执行;
来源: http://www.bubuko.com/infodetail-3259157.html