基于Guava工具类实现限流
Google开源工具包Guava提供了限流工具类RateLimiter,该类基于令牌桶算法实现流量限制,结合aop来实现
引入相关依赖
<!--包含spring aop 与 aspectj-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
编写注解和切面类
自定义注解
/**
* 访问次数校验
*
* @author eden
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitCheck {
/**
* 最多的访问限制次数
*/
double permitsPerSecond() default 5.0d;
/**
* 得不到令牌的提示语
*/
String message() default "系统繁忙,请稍后再试.";
}
编写切面类
/**
* 请求拦截 AOP
*
* @author eden
*/
@Aspect
@Component
@Slf4j
public class LimitInterceptor {
/**
* 不同的接口,不同的流量控制
* map的key为 Limiter.key
*/
private static final Map<String, RateLimiter> LIMIT_MAP = Maps.newConcurrentMap();
/**
* 执行拦截
* 获取请求参数 Object[] args = point.getArgs();
*/
@Around("@annotation(limitCheck)")
public Object doInterceptor(ProceedingJoinPoint point, LimitCheck limitCheck) throws Throwable {
// 1.获取切入点所在目标对象名称 + 方法名
String key = point.getSignature().toShortString();
RateLimiter rateLimiter;
if (!LIMIT_MAP.containsKey(key)) {
// 2 创建令牌桶
rateLimiter = RateLimiter.create(limitCheck.permitsPerSecond());
LIMIT_MAP.put(key, rateLimiter);
log.info("新建了令牌桶={},容量={}", key, limitCheck.permitsPerSecond());
}
// 取出
rateLimiter = LIMIT_MAP.get(key);
// 拿不到令牌,直接返回异常提示 tryAcquire() 判断时候能获取到1个令牌, 如果不能获取立即返回 false
if (!rateLimiter.tryAcquire()) {
log.error("令牌桶={},获取令牌失败:{}", key, limitCheck.message());
return limitCheck.message();
}
// 执行原方法
return point.proceed();
}
}
使用注解
@LimitCheck(permitsPerSecond = 2.0d)
@GetMapping("test/limit")
public String testLimit() {
return "success";
}