配置redis
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory){
return new StringRedisTemplate(factory);
}
@Bean
public DefaultRedisScript loadRedisScript(){
DefaultRedisScript redisScript=new DefaultRedisScript();
//设置lua脚本
redisScript.setLocation(new ClassPathResource("redisLimit.lua"));
//设置返回类型
redisScript.setResultType(java.lang.Boolean.class);
return redisScript;
}
}
编写redisLimit.lua脚本并放到项目的resource目录下
-- 查询key值如果大于0,则减一
-- tonumber 把字符串转成数值
if tonumber(redis.call('GET', KEYS[1])) > 0 then
redis.call('DECR', KEYS[1])
return true
else
return false
end
@RestController
@RequestMapping("/test")
public class UserController {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private DefaultRedisScript<Boolean> loadRedisScript;
@RequestMapping("/test")
public void test() {
Boolean flag = redisTemplate.execute(loadRedisScript, Arrays.asList("number"));
System.out.println(flag);
}
}
限流脚本1:
-- 在lua脚本中,有两个全局的变量,是用来接收redis应用端传递的键值和其它参数的,
-- 分别为KEYS、ARGV。
-- 在应用端传递给KEYS时是一个数组列表,在lua脚本中通过索引方式获取数组内的值。
-- 在应用端,传递给ARGV的参数比较灵活,可以是多个独立的参数,但对应到Lua脚本中是,
-- 统一用ARGV这个数组接收,获取方式也是通过数组下标获取。
-- tonumber 把字符串转成数值
local key = KEYS[1]
local max = tonumber(ARGV[1])
-- 如果key不存在设置key并设置过期时间
if tonumber(redis.call('EXISTS', key)) == 0 then
redis.call('SETNX', key, 0)
redis.call('EXPIRE', key, 60)
end
local count = tonumber(redis.call('GET', key))
if count + 1 <= max then
-- 没有超过阈值,设置当前访问的数量+1
redis.call('INCR', key)
return true
else
return false
end
限流脚本2:
local key = KEYS[1]
local nowTime = tonumber(ARGV[1])
local unitExpiredTime = tonumber(ARGV[2])
local max = tonumber(ARGV[3])
local expiredTime = nowTime - unitExpiredTime
-- 移除过期时间之前的数据
redis.call('ZREMRANGEBYSCORE', key, 0, expiredTime)
-- 获取集合元素数量
local count = tonumber(redis.call('ZCARD', key))
if count + 1 > max then
return false
else
-- 注意每个请求的value值不能一重复
-- 添加参数并设置过期时间
redis.call('ZADD', key, nowTime, nowTime)
redis.call('PEXPIRE', key, unitExpiredTime)
return true
end