赞
踩
限制某sql在30秒内最多只能执行3次
微服务分布式部署,既然是分布式限流,首先自然就想到了结合redis的zset数据结构来实现。
分析对zset的操作,有几个步骤,首先,判断zset中符合rangeScore的元素个数是否已经达到阈值,如果未达到阈值,则add元素,并返回true。如果已达到阈值,则直接返回false。
首先,我们需要根据需求编写一个lua脚本
redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, tonumber(ARGV[3]))
local res = 0
if(redis.call('ZCARD', KEYS[1]) < tonumber(ARGV[5])) then
redis.call('ZADD', KEYS[1], tonumber(ARGV[2]), ARGV[1])
res = 1
end
redis.call('EXPIRE', KEYS[1], tonumber(ARGV[4]))
return res
ARGV[1]: zset element
ARGV[2]: zset score(当前时间戳)
ARGV[3]: 30秒前的时间戳
ARGV[4]: zset key 过期时间30秒
ARGV[5]: 限流阈值
private final RedisTemplate<String, Object> redisTemplate;
public boolean execLuaScript(String luaStr, List<String> keys, List<Object> args){
RedisScript<Boolean> redisScript = RedisScript.of(luaStr, Boolean.class)
return redisTemplate.execute(redisScript, keys, args.toArray());
}
测试一下效果
@SpringBootTest public class ApiApplicationTest { @Test public void test2() throws InterruptedException{ String luaStr = "redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, tonumber(ARGV[3]))\n" + "local res = 0\n" + "if(redis.call('ZCARD', KEYS[1]) < tonumber(ARGV[5])) then\n" + " redis.call('ZADD', KEYS[1], tonumber(ARGV[2]), ARGV[1])\n" + " res = 1\n" + "end\n" + "redis.call('EXPIRE', KEYS[1], tonumber(ARGV[4]))\n" + "return res"; for (int i = 0; i < 10; i++) { boolean res = execLuaScript(luaStr, Arrays.asList("aaaa"), Arrays.asList("ele"+i, System.currentTimeMillis(),System.currentTimeMillis()-30*1000, 30, 3)); System.out.println(res); Thread.sleep(5000); } } }
测试结果符合预期!
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。