1. 程式人生 > 實用技巧 >aop切面實現一定時間內限制介面訪問次數

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>