/**
* redis 基于时间滑动窗口,进行限流逻辑
*
* @param actionKeyEnum 动作标记的限流,比如秒杀seckill
* @param keyParam 业务的唯一标识
* @param period 限流的频率 多少秒内进行限流
* @param maxCount 最大限流数量
*/
public boolean limitRateActionAllowed(LimitRateActionEnum actionKeyEnum, String keyParam, int period, int maxCount) {
// 生成唯一的key
actionKeyEnum = actionKeyEnum == null ? LimitRateActionEnum.GENERATOR : actionKeyEnum;
keyParam = StringUtils.isEmpty(keyParam) ? "0" : keyParam;
String key = String.format(RedisKeyCnst.LIMIT_RATE, actionKeyEnum.getName(), keyParam);
long nowTs = System.currentTimeMillis();
// 一定要确保唯一,纳秒时间都有可能会导致不唯一
String uuid = UUID.randomUUID().toString();
RedisSerializer<String> serializer = stringRedisTemplate.getStringSerializer();
// 使用管道
List<Object> executeResultList = stringRedisTemplate.executePipelined((RedisCallback<Long>) connection -> {
//stringRedisTemplate.executePipelined((RedisCallback<Long>) connection -> {
connection.multi();
// 添加当前操作当zset中
connection.zAdd(serializer.serialize(key), nowTs, serializer.serialize(uuid));
// 整理zset,删除时间窗口外的数据, 删除 0 到 nowTs - period * 1000 之间的数据, 返回删除的数量
connection.zRemRangeByScore(serializer.serialize(key), 0, nowTs - period * 1000);
//当前存在的数量: 可以认为是流量数量
Long count = connection.zCard(serializer.serialize(key));
//重新设置key的过期时间
connection.expire(serializer.serialize(key), period + 1);
connection.exec();
connection.close();
return null;
}, stringRedisTemplate.getValueSerializer());
Integer count = 0;
// [[true,0,6,true]]
//true: zAdd的执行结果 ,
// 0:zRemRangeByScore的执行结果,本次已删除的数量,
// 6:count的执行结果,当前流量总数,
// true: 设置key的过期时间
//String executeResultListStr = JSONObject.toJSONString(executeResultList);
//System.out.println(executeResultListStr);
log.info("基于时间滑动窗口,进行限流结果:{}", JSONObject.toJSONString(executeResultList));
Object exeResultList = executeResultList.get(0);
JSONArray resultSingleList = JSONObject.parseArray(JSONObject.toJSONString(exeResultList));
count = (Integer) resultSingleList.get(2);
return count <= maxCount;
}
redis 基于时间窗口的限流
最新推荐文章于 2024-08-11 03:20:09 发布