springBoot使用guava的令牌桶机制实现限流

      guava是谷歌提供的一RateLimiter,指定一个qps的值,请求来需要acquire获取令牌,直到令牌重新填充才得到放行。tryAcquire方法的话,可以指定一个等待时间,并返回一个Boolea值。套框架,我们这里需要用到的是它的限流器:不足之处:所有的请求进来都是调用acquire。无法根据ip或者其他的类型关键字来区分。所以我们引入了缓存,类似HashMap,针对不同的关键字(本文使用方法名)生成不同的限流器。

1、引入配置依赖

低版本没有RateLimiter方法,版本不对时可升一下版本号


    com.google.guava
    guava
    18.0

2、注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {
    int NOT_LIMITED = 0;

    @AliasFor("qps")
    double value() default NOT_LIMITED;


    @AliasFor("value")
    double qps() default NOT_LIMITED;

    /**
     * 超时时长
     */
    int timeout() default 0;


    /**
     * 超时时间单位
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

3、切面

@Slf4j
@Aspect
@Component
public class RateLimitAspect {
    private static final ConcurrentMap RATE_LIMITER_CACHE = new ConcurrentHashMap<>(16);
    @Pointcut("@annotation(com.example.springbootdemoratelimitguava.annotation.RateLimit)")
    public void rateLimit() {

    }
    @Around("rateLimit()")
    public Object pointcut(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = methodSignature.getMethod();
        RateLimit rateLimit = AnnotationUtils.findAnnotation(method, RateLimit.class);
        if (Objects.nonNull(rateLimit) && rateLimit.qps() > RateLimit.NOT_LIMITED) {
            double qps = rateLimit.qps();
            // 缓存池中是否有对象 没有则初始化刷入
            if (Objects.isNull(RATE_LIMITER_CACHE.get(method.getName()))) {
                RATE_LIMITER_CACHE.put(method.getName(), RateLimiter.create(qps));
            }
            log.debug("[{}]的qps设置为:{}", method.getName(), RATE_LIMITER_CACHE.get(method.getName()).getRate());
            // 尝试获取令牌
            if (Objects.nonNull(RATE_LIMITER_CACHE.get(method.getName())) && !RATE_LIMITER_CACHE.get(method.getName()).tryAcquire(rateLimit.timeout(), rateLimit.timeUnit())) {
                throw new RuntimeException("请求次数过多,请稍后再试");
            }
        }
        return proceedingJoinPoint.proceed();
    }
}

4、全局异常捕捉

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    public Dict handler(RuntimeException exception) {
        return Dict.create().set("msg", exception.getMessage());
    }
}

5、测试方法

@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
    @RateLimit(value = 1.0, timeout = 300)
    @GetMapping("/test1")
    public Dict test1() {
        log.info("【test1】被执行了。。。。。");
        return Dict.create().set("msg", "hello,world!").set("description", "别想一直看到我,不信你快速刷新看看~");
    }

    @GetMapping("/test2")
    public Dict test2() {
        log.info("【test2】被执行了。。。。。");
        return Dict.create().set("msg", "hello,world!").set("description", "我一直都在,卟离卟弃");
    }

    @RateLimit(value = 2.0, timeout = 300)
    @GetMapping("/test3")
    public Dict test3() {
        log.info("【test3】被执行了。。。。。");
        return Dict.create().set("msg", "hello,world!").set("description", "别想一直看到我,不信你快速刷新看看~");
    }
}

你可能感兴趣的:(java框架学习,java)