aop切面实现一定时间内限制接口访问次数

还是查svn的tags信息,框架里封装的svn接口效率实在有点墨迹,连续点个几次就挂了。

首先我想的是做缓存,查出所有的项目tags放到redis,定时任务定期更新svn数据,但是时效性不行,做到5分钟一updata还是满足不了一群程序员的需求(内部小工具);
然后我又去找svn提供的其他接口,大都不尽人意,效率上七七八八;
最后还是选择了做接口访问次数限制,通过限制访问频率控制接口不会因为连续访问svn连接超时或访问超时挂掉。

原理:自定义注解,把注解添加到我们的接口上;定义一个切面,执行方法前去ExpiringMap查询该IP在规定时间内请求了多少次,如超过次数则直接返回请求失败。

1.自定义一个注解RequestLimit

1 @Documented
2 @Target(ElementType.METHOD) // 说明该注解只能放在方法上面
3 @Retention(RetentionPolicy.RUNTIME)
4 public @interface RequestLimit {
5     long time() default 2000; // 限制时间 单位:毫秒
6     int count() default 1; // 允许请求的次数
7 }

2.自定义切面判定访问频率

 1 @Aspect
 2 @Component
 3 public class RequestLimitAspect {
 4 
 5     private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> book = new ConcurrentHashMap<>();
 6 
 7     // 定义切点
 8     // 让所有有@LimitRequest注解的方法都执行切面方法
 9     @Pointcut("@annotation(requestLimit)")
10     public void excudeService(RequestLimit requestLimit) {
11     }
12 
13     @Around("excudeService(requestLimit)")
14     public ResultVo doAround(ProceedingJoinPoint pjp, RequestLimit requestLimit) throws Throwable {
15 
16         // 获得request对象
17         RequestAttributes ra = RequestContextHolder.getRequestAttributes();
18         ServletRequestAttributes sra = (ServletRequestAttributes) ra;
19         HttpServletRequest request = sra.getRequest();
20 
21         // 获取Map对象, 如果没有则返回默认值
22         // 第一个参数是key, 第二个参数是默认值
23         ExpiringMap<String, Integer> uc = book.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());
24         Integer uCount = uc.getOrDefault(request.getRemoteAddr(), 0);
25 
26 
27 
28         if (uCount >= requestLimit.count()) { // 超过次数,不执行目标方法
29             return Result.error(ResultEnum.FAILED.getCode(),"请求频率太快啦~~");
30         } else if (uCount == 0){ // 第一次请求时,设置有效时间
31 //            /** Expires entries based on when they were last accessed */
32 //            ACCESSED,
33 //            /** Expires entries based on when they were created */
34 //            CREATED;
35             uc.put(request.getRemoteAddr(), uCount + 1, ExpirationPolicy.CREATED, requestLimit.time(), TimeUnit.MILLISECONDS);
36         } else { // 未超过次数, 记录加一
37             uc.put(request.getRemoteAddr(), uCount + 1);
38         }
39         book.put(request.getRequestURI(), uc);
40 
41         // result的值就是被拦截方法的返回值
42         Object result = pjp.proceed();
43 
44         return Result.success(result);
45     }
46 
47 }

3.接口加上注解即可食用

1 @GetMapping("/getTags")
2 @RequestLimit(count = 2)
3 public ResultVo<List<String>> getProjectTags() {
4     return Result.success();
5 }

用到的maven依赖

     <!-- AOP依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.1.5.RELEASE</version>
        </dependency>
        <!-- Map依赖 -->
        <dependency>
            <groupId>net.jodah</groupId>
            <artifactId>expiringmap</artifactId>
            <version>0.5.8</version>
        </dependency>

 

posted @ 2020-11-23 17:05  空指针终结者  阅读(500)  评论(1)    收藏  举报