1. 程式人生 > 其它 >自定義註解實現介面限流

自定義註解實現介面限流

自定義註解實現介面限流

場景:限制驗證碼在單位時間內的訪問次數
實現流程:自定義一個註解,註解內包含訪問的次數與單位時間。通過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;
    }
}