概述
guava是谷歌提供的一RateLimiter,指定一个qps的值,请求来需要acquire获取令牌,直到令牌重新填充才得到放行。tryAcquire方法的话,可以指定一个等待时间,并返回一个Boolea值。套框架,我们这里需要用到的是它的限流器:不足之处:所有的请求进来都是调用acquire。无法根据ip或者其他的类型关键字来区分。所以我们引入了缓存,类似HashMap,针对不同的关键字(本文使用方法名)生成不同的限流器。
1、引入配置依赖
低版本没有RateLimiter方法,版本不对时可升一下版本号
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency>
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<String, RateLimiter> 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", "别想一直看到我,不信你快速刷新看看~"); } }
最后
以上就是有魅力冷风为你收集整理的springBoot使用guava的令牌桶机制实现限流的全部内容,希望文章能够帮你解决springBoot使用guava的令牌桶机制实现限流所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复