增加注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RataLimit {
/**
* 时间间隔内允许通过的请求数
**/
int count() default 120;
/**
* 时间间隔,单位秒
**/
int intervalInSeconds() default 60;
/**
* 限流规则,默认使用请求方法+请求IP限流
**/
String rule() default "";
}
使用注解
@PostMapping("/logout")
@RataLimit(count = 100, intervalInSeconds = 3600)
public ResultDTO<String> logout(){
userServiceImpl.logout();
return ResultDTO.newSuccessResult();
}
@Aspect的实现
@Aspect
@Component
public class RateLimitAspect {
private static final ThorExtLogger logger = ThorExtLogger.create(RateLimitAspect.class);
private static final String REDIS_RATE_LIMIT_TEMPLATE = BaseConstant.STR_PLACEHOLDER_CHARACTER
.concat(BaseConstant.REDIS_SPLITTING_CHARACTER)
.concat(BaseConstant.STR_PLACEHOLDER_CHARACTER);
@Autowired
private CacheRepository cacheRepository;
@Pointcut(value = "@annotation(com.duxiaoman.pay.epic.spfront.annotation.RataLimit)")
//这里表示所用使用rateLimit注解的方法,注意PointCut切入点只能是方法
public void pointcut() {
}
@Before("pointcut() && @annotation(rataLimit)")
//@Before方法中pointcut()代表的是对拦截到方法执行的逻辑而@annotation(rataLimit)表示可以在下面的方法中获取到你在方法上使用RateLimit的参数
public void before(JoinPoint joinPoint, RataLimit rataLimit) throws Throwable {
//JoinPoint joinPoint:
//JoinPoint 是 Spring AOP 提供的一个接口,它提供了有关当前被拦截的方法的信息。通过 JoinPoint,你可以获取被拦截方法的签名、参数、类实例等。
//常用的方法:
//joinPoint.getSignature():获取被拦截方法的签名(方法名称、返回类型等)。
//joinPoint.getArgs():获取被拦截方法的参数。
//joinPoint.getTarget():获取执行方法的目标对象(即当前类的实例)。
logger.info("Enter rateLimitAspect @Before method.");
String method = joinPoint.getSignature().getName();
String ipAddress = HttpRequestUtil.getRemoteRealIpAddr(ServletUtils.getRequest());
String rule = rateLimit.rule();
String redisKey = MessageFormatter.format(REDIS_RATE_LIMIT_TEMPLATE, method, ipAddress).getMessage();
String key = StringUtils.isNotBlank(rule) ? rule : redisKey;
/** 请求次数限制 **/
int permitsInterval = rataLimit.count();
/** 时间间隔 **/
int interval = rataLimit.intervalInSeconds();
logger.info("RateLimitAspect @Before method output count: {}, intervalInSeconds: {}, rule: {}",
permitsInterval, interval, rule);
boolean isPermit = cacheRepository.checkAndIncrementThreshold(key, permitsInterval, interval);
/** 指定时间间隔内,超过最大频率限制,进行限流 **/
if (!isPermit) {
logger.error("RateLimitAspect @Before method output. Call proxy method {} reaches {} maximum current limit",
method, permitsInterval);
throw new CommonException(ErrorCodeEnum.IP_REQUEST_UP_LIMIT_ERROR);
}
logger.info("Exit from rateLimitAspect @Before method.");
}
}