SpringBoot整合RateLimiter实现限流

简介: 写作缘由 在和某学长炫耀在自己会用Redis+Lua实现滑动窗口限流时,他说现在都用RateLimiter,所以就我就想搞个Demo,但是度娘了一下,感觉我搜索到的博客有几个个人认为不太完善的地方,比如只贴了部分代码,没贴依赖。尤其是你用AOP实现的时候,其实依赖哪个还有有讲究的;还有一个问题就是大多都是基于AOP实现,拦截器实现也是一个不错的方式,所以此处用拦截器HandlerInterceptorAdapter实现。

源码下载


https://github.com/cbeann/Demooo/tree/master/springboot-ratelimiter


部分代码


pom


 <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.1-jre</version>
        </dependency>


自定义注解ExtRateLimiter


package com.example.annotation;
import java.lang.annotation.*;
/**
 * @author chaird
 * @create 2021-03-20 17:57
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtRateLimiter {
  // 以每秒为单位固定的速率值往令牌桶中添加令牌
  double permitsPerSecond();
  // 在规定的毫秒数中,如果没有获取到令牌的话,则直接走服务器降级处理
  long timeout();
}


拦截器


package com.example.Interceptor;
import com.example.annotation.ExtRateLimiter;
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
 * @author CBeann
 * @create 2020-07-04 18:06
 */
@Component
public class RateLimiterInceptor extends HandlerInterceptorAdapter {
  private Map<String, RateLimiter> rateHashMap = new ConcurrentHashMap<>();
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    if (!(handler instanceof HandlerMethod)) {
      return true;
    }
    final HandlerMethod handlerMethod = (HandlerMethod) handler;
    final Method method = handlerMethod.getMethod();
    // 有这个注解
    boolean methodAnn = method.isAnnotationPresent(ExtRateLimiter.class);
    if (methodAnn) {
      // 获取注解
      ExtRateLimiter extRateLimiter = method.getDeclaredAnnotation(ExtRateLimiter.class);
      //获取注解属性
      double permitsPerSecond = extRateLimiter.permitsPerSecond();
      long timeout = extRateLimiter.timeout();
      String key = method.getDeclaringClass().getName() + method.getName();
      RateLimiter rateLimiter = null;
      if (rateHashMap.get(key) == null) {
        rateLimiter = RateLimiter.create(permitsPerSecond);
        rateHashMap.put(key, rateLimiter);
      } else {
        rateLimiter = rateHashMap.get(key);
      }
      boolean tryAcquire = rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS);
      if (!tryAcquire) {
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("限流");
        writer.close();
        response.flushBuffer();
        return false;
      }
      return super.preHandle(request, response, handler);
    }
    return true;
  }
  @Override
  public void postHandle(
      HttpServletRequest request,
      HttpServletResponse response,
      Object handler,
      ModelAndView modelAndView)
      throws Exception {
    super.postHandle(request, response, handler, modelAndView);
  }
}


拦截器配置


package com.example.config;
import com.example.Interceptor.RateLimiterInceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
 * @author chaird
 * @create 2020-09-23 16:13
 */
@Configuration
public class MVCConfig extends WebMvcConfigurerAdapter {
  @Autowired private RateLimiterInceptor rateLimiterInceptor;
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    // 获取http请求拦截器
    registry.addInterceptor(rateLimiterInceptor).addPathPatterns("/*");
  }
}


controller


package com.example.controller;
import com.example.annotation.ExtRateLimiter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StockController {
  @GetMapping("/getStock")
  @ExtRateLimiter(permitsPerSecond = 2, timeout = 500)
  public Object getStock() {
    String s = "ok";
    return s;
  }
}


测试


大约每秒允许2个请求


http://localhost:8080/getStock

目录
相关文章
|
2月前
|
缓存 Java Sentinel
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
|
10月前
|
Java
springboot实现自定义注解限流
springboot实现自定义注解限流
115 1
|
12天前
|
NoSQL 算法 Java
使用 Spring Boot 实现限流功能:从理论到实践
【6月更文挑战第18天】在微服务和高并发系统中,限流(Rate Limiting)是一种非常重要的技术手段,用于保护系统免受过载,确保服务的稳定性。限流可以控制请求的速率,防止单个客户端或恶意用户消耗过多的资源,从而影响其他用户。
16 5
|
2月前
|
算法 NoSQL Java
springboot整合redis及lua脚本实现接口限流
springboot整合redis及lua脚本实现接口限流
134 0
|
监控 Java API
SpringBoot 2.0 + 阿里巴巴 Sentinel 动态限流实战
前言 在从0到1构建分布式秒杀系统和打造十万博文系统中,限流是不可缺少的一个环节,在系统能承受的范围内既能减少资源开销又能防御恶意攻击。 在前面的文章中,我们使用了开源工具包 Guava 提供的限流工具类 RateLimiter 和 OpenResty 的 Lua 脚本分别进行 API 和应用层面的限流。
2786 0
|
2月前
|
前端开发 NoSQL Java
【SpringBoot】秒杀业务:redis+拦截器+自定义注解+验证码简单实现限流
【SpringBoot】秒杀业务:redis+拦截器+自定义注解+验证码简单实现限流
|
2月前
|
存储 算法 Java
Spring Boot 通用限流方案
Spring Boot 通用限流方案
221 0
|
2月前
|
算法 NoSQL Java
8. 「Java大师」教你如何用Spring Boot轻松实现高效「限流」!
8. 「Java大师」教你如何用Spring Boot轻松实现高效「限流」!
178 0
|
10月前
|
NoSQL Java Redis
springboot高级教程基于 redis 通过注解实现限流
springboot高级教程基于 redis 通过注解实现限流
115 0
|
12月前
|
NoSQL 前端开发 Java
Springboot搭配Redis实现接口限流
介绍 限流的需求出现在许多常见的场景中: 秒杀活动,有人使用软件恶意刷单抢货,需要限流防止机器参与活动 某 api 被各式各样系统广泛调用,严重消耗网络、内存等资源,需要合理限流 淘宝获取 ip 所在城市接口、微信公众号识别微信用户等开发接口,免费提供给用户时需要限流,更 具有实时性和准确性的接口需要付费。
131 0