最近写个小demo的想实现了多少秒内操作几次接口提示频繁的警告、首先的你先有个思路、我的思路呢就是百度找例子!!!
话不多说先上代码
百度例子:
//存入redis的key |每次自增多少
long count = redisTemplate.opsForValue().increment("a", 1);
if(count == 3){
//存入redis的key | 设置key为15秒 | 时间单位
redisTemplate.expire("a", 15, TimeUnit.SECONDS)
}
if(count > 3){
// 这个是我封装的响应
return R.failed(ApiErrorCode.FREQUENTLY.getCode(), "操作频繁");
}
复制代码
看完之后细心的朋友已经发现bug了吧、按照上面的流程走的话就是在请求第二次的时候count已经加一到了三然后继续走下面count == 3成立了设置key的时间为15秒过期、再次请求、也就是count从三加一变成了四此时count == 3就不成立了、则count > 3 成立、此时这样的逻辑看起来是没问题、但是就按上面的逻辑来说这种只适合多次不停点击请求响应、但是你有没有考虑间接性请求会有很大的问题!!!
问题
如:我设置key过期时间15秒内请求3次、正常来说我一次请求3次在第4次的情况下会出现提示操作频繁、但是我请求第2次的时候、我停一分钟在请求、这下就发现了问题、就是说我在一分钟后再次接上请求2次就会提示频繁操作、这种间接性请求的会导致体验很差!!!
后需改进的代码
RedisExpired :封装存数据的类
/**
* 功能描述: 用于封装频繁请求接口、将数据存入redis中使用到的对象
*
* @Author: 魏
* @Date: 2021/6/18 20:13
*/
@Data
public class RedisExpired {
// redis key
private String key;
// redis value
private Object value;
// 设置频繁操作次数
private Integer frequentlyTimes;
// 过期时间
private long ExpiredTime;
// 状态
private Boolean isFlag;
public RedisExpired(String key, Integer frequentlyTimes, long expiredTime) {
this.key = key;
this.frequentlyTimes = frequentlyTimes;
ExpiredTime = expiredTime;
}
}
复制代码
具体实现方法
public static RedisExpired addRedisExpiredTimes(RedisExpired redisExpired){
Integer i = (Integer) get(redisExpired.getKey());
if (i != null && i > redisExpired.getFrequentlyTimes()){
redisExpired.setValue(get(redisExpired.getKey()));
// 设置key的过期时间
redisExpired.setExpiredTime(getExpire(redisExpired.getKey()));
// 设置状态
redisExpired.setIsFlag(false);
return redisExpired;
}
// redis 自增1 返回自增后的数 、并设置过期时间
long count = incrBy(redisExpired.getKey(), 1,redisExpired.getExpiredTime());
// 自增后的数等于设置
if(count == redisExpired.getFrequentlyTimes() + 1){
redisExpired.setIsFlag(false);
return redisExpired;
}
redisExpired.setIsFlag(true);
return redisExpired;
}
复制代码
Service层做的操作
/**
* 添加/取消 关注 操作
* @param accountId 用户id
* @param attentionId 关注的id
* @param flag 当前关注的状态 true 关注 false 未关注
*/
@Override
public R<Object> addOrCancellFollow(String accountId, String attentionId, boolean flag) {
RedisExpired redisExpired = RedisUtil.addRedisExpiredTimes(new RedisExpired(
StrUtil.format("{}::{}", AccountUtils.getLoginAccount().getName(), accountId),
3, 15));
if (!redisExpired.getIsFlag()) {
return R.failed(ApiErrorCode.FREQUENTLY.getCode(), StrUtil.format("频繁操作请{}在试", TimeUtils.formatDateTime(redisExpired.getExpiredTime())));
}
if (flag){
// 添加关注
fansMapper.attentionAccount(accountId,attentionId);
return R.success(true,"关注成功");
}
// 取消关注
fansMapper.cancelAttentionAccount(accountId,attentionId);
return R.success(false,"取消关注");
}
复制代码
上面的只是我自己封装的而已、最主要的就是incrBy()方法
/**
* 增加(自增长), 负数则为自减
*
* @param key
* @return
*/
public static Long incrBy(String key, long increment,long time) {
if (time != 0){
Long aLong = MyRedisTemplate.opsForValue().increment(key, increment);
expire(key, time, TimeUnit.SECONDS);
return aLong;
}
return MyRedisTemplate.opsForValue().increment(key, increment);
}
复制代码
解决间接性请求就是在redis自增的后设置过期时间。
具体流程图
- 改进后的代码有两个改变
- 1、在达到指定次数后在不进行redis增加方法、直接返回频繁操作。
- 2、间接性请求。(每次redis自增的时候设置key的缓存时间)
- 最后实现的功能就是在多少秒内允许请求多少次、在时间过期后能再次能请求、允许间接性请求。
想法1
还可以改变就是说、我们将第一个改变去掉(在达到指定次数后在不进行redis增加方法、直接返回频繁操作。)没有这个添加的话redis就会一直请求增加、如在15秒内请求、100次的情况下我们给他下次请求必须的等三分钟、200次就9分钟、500次我们直接将账号封禁!!!
想法2
通过AOP的方式来指定某些请求限制请求多少秒中请求多少次、AOP前置通知来实现
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END