自定義註解實現介面限流
阿新 • • 發佈:2022-03-14
自定義註解實現介面限流
場景:限制驗證碼在單位時間內的訪問次數
實現流程:自定義一個註解,註解內包含訪問的次數與單位時間。通過AOP進行切面攔截,獲取註解內的次數和時間,獲取請求的uri與訪問者ip。組成redis的key。
使用redis將key進行原子性自增1.如果返回的是1.則設定過期時間,之後的訪問直接incr即可。但是這種方式存在問題,不是原子性的,所以需要採用原子性的設定key和過期時間。
Redis官方目前set命令已經支援了set的時候指定是nx,並且設定過期時間合併成了一條指令,保證了原子性。
因此採用setIfAbsent()API進行設定。如果返回的true。代表首次設定key。如果已經存在,則返回false。代表key已經存在。則使用incr進行自增即可。
程式碼實現
/** * 處理多次請求問題,限制指定時間內只能訪問的次數 */ @Aspect @Component public class ReqLimitSAspect { @Autowired private RedisTemplate redisTemplate; @Around("@annotation(com.tute.edu.planetcommunity.annotation.ReqLmit)") public Object hanlderReqLimit(ProceedingJoinPoint point) { MethodSignature methodSignature = (MethodSignature) point.getSignature(); ReqLmit annotation = methodSignature.getMethod().getAnnotation(ReqLmit.class); long count = annotation.count(); int time = annotation.time(); RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); //獲取訪問者的ip //獲取訪問的uri String requestURI = request.getRequestURI(); String ipAddr = IpUtils.getIpAddr(request); String key = "req:limit:" + ipAddr + ":" + requestURI; Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(key, 1, time, TimeUnit.SECONDS); Long countIncr=0L; if (!aBoolean) { countIncr = redisTemplate.opsForValue().increment(key); } if (countIncr > count) { throw new CustomException("呼叫頻繁,請稍後再試!"); } try { return point.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } return null; } }