SpringbootGuava+AOP接口限流

SpringbootGuava+AOP接口限流

1.Springboot Guava工具类RateLimiter+AOP限流

1. 添加依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1-jre</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 创建注解

/**
 * @author devcxl
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Limit {


    /**
     * 拦截标识
     * 
     * @return
     */
    String key() default "";
    /**
     * 次数
     *
     * @return
     */
    int max() default 1;

    /**
     * 最大等待时间
     *
     * @return
     */
    int timeout() default 5;

    /**
     * 最大等待时间时间单位
     *
     * @return
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

    /**
     * 提示
     *
     * @return
     */
    String msg() default "系统繁忙,请稍后再试.";
}

3. AOP实现

@Slf4j
@Aspect
@Component
public class LimitAspect {

    /**
     * 内存中记录限制
     */
    private Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();

    @Around("@annotation(cn.devcxl.common.annotation.guava.Limit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        Limit limit = method.getAnnotation(Limit.class);
        if (limit != null) {
            String key=limit.key();
            RateLimiter rateLimiter = null;
            //不存在直接新建
            if (!limitMap.containsKey(key)) {
                rateLimiter = RateLimiter.create(limit.max());
                limitMap.put(key, rateLimiter);
                log.info("Limit:{},max:{}",key,limit.max());
            }
            rateLimiter = limitMap.get(key);
            boolean acquire = rateLimiter.tryAcquire(limit.timeout(), limit.timeUnit());
            // 拿不到直接抛异常
            if (!acquire) {
                // RuntimeException 可以改为自定义异常 可在全局异常捕获中处理并返回给前端
                throw new RuntimeException(limit.msg());
            }
        }
        return joinPoint.proceed();
    }
}

4. 使用

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping("/limit")
    @Limit(key = "test1", max = 1, timeout = 300, timeunit = TimeUnit.MILLISECONDS, msg = "系统繁忙,请稍后再试。")
    public Resp<String> test1() {
        log.info("test1");
        return Resp.ok();
    }
}