对某个接口进行限流 以 Aop 注解的形式绑定接口 用redis实现

发布时间 2023-09-21 11:31:07作者: 纪纪纪纪纪
简单的针对某个接口进行限流,如果需要整体限流的话还是建议在网关上面或者服务器上面动手
Controller:


  @LimitRequest(count = 1,time = 60 * 1000 * 2)
  @PostMapping("limit")
  public String getLimitResult() {
   return "ok";
  }
Annotation
  
  @Retention(RetentionPolicy.RUNTIME) // 运行时
  @Target({ElementType.TYPE, ElementType.METHOD}) // 可以被用在类及方法上
  @Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级
  public @interface LimitRequest {

  /**
   * @return 允许的请求次数,默认 MAX_VALUE
   */
   int count() default Integer.MAX_VALUE;

  /**
   * @return 时间段,单位为毫秒数(和redis统一),默认 1分钟
   */
  int time() default 60 * 1000;
  
  /**
   * @return 触发限流之后抛出的异常信息
   */
   String meg() default "登记太频繁,请2分钟之后再次登记!";
  }

AOP:

  @Slf4j
  @Aspect
  @Component
  @Order(1)
  public class LimitRequestAop {
   @Autowired
   private RedisTemplate<String, String> redisTemplate;
  
   @Around("@annotation(limitRequest)")
  public Object doAround(ProceedingJoinPoint joinPoint, LimitRequest limitRequest) throws Throwable {
   // 获取当前请求对象
   ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
   boolean flag = true;
   HttpServletRequest request = attributes.getRequest();
      // String ipAddr = request.getRemoteAddr(); //根据自己需求来确定下面拼redis key值的时候要不要拼上客户端的ip
      // logger.info("ip" + ipAddr);
   String uri = request.getRequestURI();
  if (limitRequest != null) {
   flag = validRequestCount( uri.replace("/", "_"), limitRequest.count(), limitRequest.time());
   }
   if (flag) {
   return joinPoint.proceed();
   }
  
   throw new RuntimeException(limitRequest.meg());
   }

   /**
   * 判断某一个ip请求接口的次数是否超过限制
   *
  * @param uri 接口路径
  * @param limitCount 限流大小
  * @param timeOut 判断限流的时间
   * @return true 未限流 false 限流
   */
   private boolean validRequestCount(String uri, int limitCount, long timeOut) {
   try {
   // /api/queryInfo
   String redisKey = "req_limit_".concat(uri);
   long count = redisTemplate.opsForValue().increment(redisKey, 1);
   if (count == 1) {
   redisTemplate.expire(redisKey, timeOut, TimeUnit.MILLISECONDS);
  }
   if (count > limitCount) {
   return false;
  }
   } catch (Exception e) {
   return false;
   }
   return true;
   }


  }