SpringBoot使用自定义注解+AOP+Redis实现接口限流()

  本篇文章为你整理了SpringBoot使用自定义注解+AOP+Redis实现接口限流()的详细内容,包含有 SpringBoot使用自定义注解+AOP+Redis实现接口限流,希望能帮助你了解 SpringBoot使用自定义注解+AOP+Redis实现接口限流。

  为什么要限流

  系统在设计的时候,我们会有一个系统的预估容量,长时间超过系统能承受的TPS/QPS阈值,系统有可能会被压垮,最终导致整个服务不可用。为了避免这种情况,我们就需要对接口请求进行限流。

  所以,我们可以通过对并发访问请求进行限速或者一个时间窗口内的的请求数量进行限速来保护系统或避免不必要的资源浪费,一旦达到限制速率则可以拒绝服务、排队或等待。
 

  

  限流背景

  系统有一个获取手机短信验证码的接口,因为是开放接口,所以为了避免用户不断的发送请求获取验证码,防止恶意刷接口的情况发生,于是用最简单的计数器方式做了限流,限制每个IP每分钟只能请求一次,然后其他每个手机号的时间窗口限制则是通过业务逻辑进行判断。一般一些接口访问量比较大的,可能会压垮系统的,则需要加入流量限制!如:秒杀等...

  

  实现限流

  1、引入依赖

  

 dependency 

 

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-aop /artifactId

   /dependency

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-data-redis /artifactId

   /dependency

 

  

  2、自定义限流注解

  

@Target(ElementType.METHOD)

 

  @Retention(RetentionPolicy.RUNTIME)

  @Documented

  public @interface RateLimiter

   * 限流key

   String key() default Constants.RATE_LIMIT_KEY;

   * 限流时间,单位秒

   int time() default 60;

   * 限流次数

   int count() default 100;

   * 限流类型

   LimitType limitType() default LimitType.DEFAULT;

   * 限流后返回的文字

   String limitMsg() default "访问过于频繁,请稍候再试";

  }

 

  

  3、限流切面

  

@Aspect

 

  @Component

  public class RateLimiterAspect {

   private final static Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);

   @Autowired

   private RedisUtils redisUtils;

   @Before("@annotation(rateLimiter)")

   public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable

   int time = rateLimiter.time();

   int count = rateLimiter.count();

   long total = 1L;

   String combineKey = getCombineKey(rateLimiter, point);

   if(redisUtils.hasKey(combineKey)){

   total = redisUtils.incr(combineKey,1); //请求进来,对应的key加1

   if(total count)

   throw new ServiceRuntimeException(rateLimiter.limitMsg());

   }else{

   redisUtils.set(combineKey,1,time); //初始化key

   catch (ServiceRuntimeException e)

   throw e;

   catch (Exception e)

   throw new ServiceRuntimeException("网络繁忙,请稍候再试");

   * 获取限流key

   * @param rateLimiter

   * @param point

   * @return

   public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)

   StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());

   if (rateLimiter.limitType() == LimitType.IP)

   stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");

   MethodSignature signature = (MethodSignature) point.getSignature();

   Method method = signature.getMethod();

   Class ? targetClass = method.getDeclaringClass();

   stringBuffer.append(targetClass.getName()).append("-").append(method.getName());

   return stringBuffer.toString();

  
@RateLimiter(time = 60, count = 1, limitType = LimitType.IP, limitMsg = "一分钟内只能请求一次,请稍后重试")

   @GetMapping("/hello")

   public ResultMsg hello() {

   return ResultMsg.success("Hello World!");

  }

 

  

  5、全局异常拦截

  

@RestControllerAdvice

 

  public class GlobalExceptionHandler {

   private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

   * 业务异常

   @ExceptionHandler(ServiceRuntimeException.class)

   public ResultMsg handleServiceException(ServiceRuntimeException e, HttpServletRequest request)

   return ResultMsg.error(e.getMessage());

   * 系统异常

   @ExceptionHandler(Exception.class)

   public ResultMsg handleException(Exception e, HttpServletRequest request)

   return ResultMsg.error("系统异常");

  }

 

  

  6、接口测试

  1)第一次发送,正常返回结果

  

  2)一分钟内第二次发送,返回错误,限流提示

  

  

  好了,大功告成啦

  还有其他的限流方式,如滑动窗口限流方式(比计数器更严谨)、令牌桶等...,有兴趣的小伙伴可以学习一下

  

  附源码

  https://gitee.com/jae_1995/ratelimiter

  

  以上就是SpringBoot使用自定义注解+AOP+Redis实现接口限流()的详细内容,想要了解更多 SpringBoot使用自定义注解+AOP+Redis实现接口限流的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: