加拿大28怎么做网站代理,成都住建平台app,Lms wordpress功能,设计类专业大学在你的项目中#xff0c;有没有遇到用户重复提交的场景#xff0c;即当用户因为网络延迟等情况把已经提交过一次的东西再次进行了提价#xff0c;本篇文章将向各位介绍使用滑动窗口限流的方式来防止用户重复提交#xff0c;并通过我们的自定义注解来进行封装功能。
首先有没有遇到用户重复提交的场景即当用户因为网络延迟等情况把已经提交过一次的东西再次进行了提价本篇文章将向各位介绍使用滑动窗口限流的方式来防止用户重复提交并通过我们的自定义注解来进行封装功能。
首先导入相关依赖:
!-- 引入切面依赖--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactIdscopetest/scope/dependencydependencygroupIdorg.aspectj/groupIdartifactIdaspectjweaver/artifactId/dependency
然后我们先写一下滑动窗口限流的逻辑
//滑动窗口限流逻辑
public class RateLimiter {private static ConcurrentHashMapString, DequeLong requestTimestampsnew ConcurrentHashMap();public static boolean isAllowed(String userId,int timeWindow,int maxRequests){long now System.currentTimeMillis();long windowStartnow -(timeWindow*1000);requestTimestamps.putIfAbsent(userId,new LinkedList());DequeLong timestampsrequestTimestamps.get(userId);synchronized (timestamps){// 移除窗口外的时间戳while(!timestamps.isEmpty() timestamps.peekFirst()windowStart){timestamps.pollFirst();}// 如果时间戳数量小于最大请求数允许访问并添加时间戳if(timestamps.size()maxRequests){timestamps.addLast(now);return true;}else{return false;}}}
}
主要部分解释
1. 定义 requestTimestamps 变量
private static ConcurrentHashMapString, DequeLong requestTimestamps new ConcurrentHashMap();
requestTimestamps 是一个并发的哈希映射用于存储每个用户的请求时间戳。键String是用户ID。值DequeLong是一个双端队列用于存储用户请求的时间戳以毫秒为单位。
2. isAllowed 方法
public static boolean isAllowed(String userId, int timeWindow, int maxRequests) {
该方法接受三个参数 userId用户ID。timeWindow时间窗口单位为秒。maxRequests时间窗口内允许的最大请求数。方法返回一个布尔值表示用户是否被允许发出请求。
3. 获取当前时间和时间窗口开始时间
long now System.currentTimeMillis(); long windowStart now - (timeWindow * 1000);
now当前时间以毫秒为单位。windowStart时间窗口的开始时间即当前时间减去时间窗口长度以毫秒为单位。
4. 初始化用户的请求时间戳队列
requestTimestamps.putIfAbsent(userId, new LinkedList()); DequeLong timestamps requestTimestamps.get(userId);
requestTimestamps.putIfAbsent(userId, new LinkedList())如果 requestTimestamps 中没有该用户的记录则为其初始化一个空的 LinkedList。timestamps获取该用户对应的时间戳队列。
5. 同步时间戳队列
synchronized (timestamps) {
同步块对用户的时间戳队列进行同步以确保线程安全。
6. 移除窗口外的时间戳
while (!timestamps.isEmpty() timestamps.peekFirst() windowStart) { timestamps.pollFirst(); }
循环检查并移除队列中位于时间窗口之外的时间戳即小于 windowStart 的时间戳。
7. 检查请求数并更新时间戳队列
if (timestamps.size() maxRequests) { timestamps.addLast(now); return true; } else { return false; }
如果时间戳队列的大小小于 maxRequests说明在时间窗口内的请求次数未超过限制 将当前时间戳添加到队列的末尾。返回 true表示允许请求。否则返回 false表示拒绝请求。
接下来我们需要实现一个AOP切面来实现我们的自定义注解
Component
Aspect
public class RateLimitInterceptor {
// private HashMapString,String info;Autowiredprivate RedisTemplateString,String redisTemplate;Around(annotation(rateLimit))public Object interceptor(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {String userid redisTemplate.opsForValue().get(loginId); //获取用户IDSystem.out.println(userid:userid);int timeWindowrateLimit.timeWindow();int maxRequestsrateLimit.maxRequests();if(RateLimiter.isAllowed(userid,timeWindow,maxRequests)){return joinPoint.proceed();}else{throw new RepeatException(访问过于频繁请稍后再试);}}
} 获取用户ID的逻辑需要根据你的项目实际情况进行编写我这里是把id存在redis里面的但是也是存在问题的读者可以尝试使用RabbitMQ进行实现。
然后自定义一个注解
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
public interface RateLimit {int timeWindow() default 60; // 时间窗口大小单位为秒int maxRequests() default 10; //最大请求次数
}以上代码写好之后其实整个关键的代码就完成了你可以随便在你的项目中找一个接口试一下如下 maxRequests表示在timeWindow时间内的最大请求数 结果如下当然如果需要在前台显示可以稍微改一下异常的处理方式让提示信息能在前台显示