SpringBoot基于RateLimiter+AOP动态的为不同接口限流

此文为转载文章,按照博主流程,测试成功,所以转到该处!!!

原文章:https://blog.csdn.net/qq_39816039/article/details/83988517

 

1.首先接口限流算法:

      1.计数器方式(传统计数器缺点:临界问题 可能违背定义固定速率原则)

     2.令牌桶方式

    

      3.漏桶方式

     4.应用层限流(Nginx)

2.限流实现:

    2.1. RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率。

    2.2.导入相关依赖包

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>


<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

</dependency>


<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>


<dependency>

<groupId>com.google.guava</groupId>

<artifactId>guava</artifactId>

<version>20.0</version>

</dependency>

  2.3.代码实现不多说每一步都有注解

    2.3.1 定义注解

@Inherited

@Documented

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface RateLimit {

double limitNum() default 20; //默认每秒放入桶中的token

}

    2.3.2 封装定义返回结果


public class MyResult {

private Integer status;

private String msg;

private List<Object> data;


public MyResult(Integer status, String msg, List<Object> data) {

this.status = status;

this.msg = msg;

this.data = data;

}


public static MyResult OK(String msg, List<Object> data) {

return new MyResult(200, msg, data);

}


public static MyResult Error(Integer status, String msg) {

return new MyResult(status, msg, null);

}

 

2.3.3 aop实现

  1.  
    1. @Component
      
      @Scope
      
      @Aspect
      
      public class RateLimitAspect {
      
      private Logger log = LoggerFactory.getLogger(this.getClass());
      
      //用来存放不同接口的RateLimiter(key为接口名称,value为RateLimiter)
      
      private ConcurrentHashMap<String, RateLimiter> map = new ConcurrentHashMap<>();
      
      
      private static ObjectMapper objectMapper = new ObjectMapper();
      
      
      private RateLimiter rateLimiter;
      
      
      @Autowired
      
      private HttpServletResponse response;
      
      
      @Pointcut("@annotation(com.icat.retalimitaop.annotation.RateLimit)")
      
      public void serviceLimit() {
      
      }
      
      
      @Around("serviceLimit()")
      
      public Object around(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
      
      Object obj = null;
      
      //获取拦截的方法名
      
      Signature sig = joinPoint.getSignature();
      
      //获取拦截的方法名
      
      MethodSignature msig = (MethodSignature) sig;
      
      //返回被织入增加处理目标对象
      
      Object target = joinPoint.getTarget();
      
      //为了获取注解信息
      
      Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
      
      //获取注解信息
      
      RateLimit annotation = currentMethod.getAnnotation(RateLimit.class);
      
      double limitNum = annotation.limitNum(); //获取注解每秒加入桶中的token
      
      String functionName = msig.getName(); // 注解所在方法名区分不同的限流策略
      
      
      //获取rateLimiter
      
      if(map.containsKey(functionName)){
      
      rateLimiter = map.get(functionName);
      
      }else {
      
      map.put(functionName, RateLimiter.create(limitNum));
      
      rateLimiter = map.get(functionName);
      
      }
      
      
      try {
      
      if (rateLimiter.tryAcquire()) {
      
      //执行方法
      
      obj = joinPoint.proceed();
      
      } else {
      
      //拒绝了请求(服务降级)
      
      String result = objectMapper.writeValueAsString(MyResult.Error(500, "系统繁忙!"));
      
      log.info("拒绝了请求:" + result);
      
      outErrorResult(result);
      
      }
      
      } catch (Throwable throwable) {
      
      throwable.printStackTrace();
      
      }
      
      return obj;
      
      }
      
      //将结果返回
      
      public void outErrorResult(String result) {
      
      response.setContentType("application/json;charset=UTF-8");
      
      try (ServletOutputStream outputStream = response.getOutputStream()) {
      
      outputStream.write(result.getBytes("utf-8"));
      
      } catch (IOException e) {
      
      e.printStackTrace();
      
      }
      
      }
      
      
      static {
      
      objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
      
      }
      
      
      }

       

3.测试限流

2个接口设定没秒限流5个和美妙限流10个

@RateLimit(limitNum = 5.0)

public MyResult getResults() {

log.info("调用了方法getResults");

return MyResult.OK("调用了方法", null);

}


@RateLimit(limitNum = 10.0)

public MyResult getResultTwo() {

log.info("调用了方法getResultTwo");

return MyResult.OK("调用了方法getResultTwo", null);

}
  1. 使用Jmeter测试getResults接口 20个并发(设定每秒只能处理5个请求)

使用Jmeter测试getResultTwo接口 20个并发(设定每秒只能处理10个请求)

  结果会比设定的多一个(百度了很久没找到原因 -  -! )

下载地址:

      链接:https://pan.baidu.com/s/1yUr0-QxQoMD_XRPivuUjZw      提取码:6w3e 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值