需要导入的依赖
<!-- guava 限流 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>25.1-jre</version>
</dependency>
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
自定义Limiter注解
package com.chen.annotation;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* 限流注解
*
* @author chenchao
* @date 2022/6/29 14:51
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented // 将注解生成在javadoc中
public @interface Limiter {
int NOT_LIMIT = 0;
/**
* qps:Queries-per-second, 每秒查询率,QPS = req/sec = 请求数/秒
*/
@AliasFor("qps")
double value() default NOT_LIMIT;
@AliasFor("value")
double qps() default NOT_LIMIT;
/**
* 超时时长
*/
int timeout() default 0;
/**
* 超时时间单位
*/
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}
实现aop切面
package com.chen.config;
import com.chen.annotation.Limiter;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author chenchao
* @date 2022/6/29 15:43
*/
@Aspect
@Component
@Slf4j
public class RateLimiterAspect {
private static final ConcurrentHashMap<String, RateLimiter> LIMIT_CACHE = new ConcurrentHashMap<>();
@Pointcut("@annotation(com.chen.annotation.Limiter)")
public void pointcut() {
}
@Around("pointcut()")
public Object pointcut(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Limiter limiter = AnnotationUtils.findAnnotation(method, Limiter.class);
if (limiter != null && limiter.qps() > Limiter.NOT_LIMIT) {
double qps = limiter.qps();
if (LIMIT_CACHE.get(method.getName()) == null) {
LIMIT_CACHE.put(method.getName(), RateLimiter.create(qps));
}
log.debug("【{}】的QPS设置为: {}", method.getName(), LIMIT_CACHE.get(method.getName()).getRate());
// 尝试获取令牌
if (LIMIT_CACHE.get(method.getName()) != null &&
!LIMIT_CACHE.get(method.getName()).tryAcquire(limiter.timeout(), limiter.timeUnit())) {
// throw new RuntimeException("访问太频繁了,请稍后再试...");
log.info("访问太频繁了,请稍后再试...");
return "访问太频繁了,请稍后再试...";
}
}
return point.proceed();
}
}
controller层调用
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/demo")
@Limiter(value = 1.0, timeout = 100)
public String demo() {
return "你好,世界";
}
}
效果
