前言
最近看到有人在拾壹的文章下面刷了好多评论,小伙伴还是用心了,特意以此来提醒拾壹该加限流处理了。也是,如果网站不加限流处理,对于访问量多的网站来说,但凡有一部分人恶意刷接口,这个网站十有八九就崩了,加了限流的话在规定次数内他只能调用这么多次,后续的请求都不会放行,可以有效的抵制刷接口的人。当然,拾壹这种小网站还是不怕你们刷,毕竟崩了就崩了,无伤大雅,不过限流也还是要加的,下面就来一个简单的限流处理吧。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
自定义注解的话就肯定需要使用到切面了,所以需要引入aop,然后又是通过redis来限流,所以还需要引入redis
创建AccessLimit注解文件
/**
* @title: AccessLimit
* @description: 限流注解
*/
@Documented
@Target(ElementType.METHOD)//注解放置的目标位置即方法级别
@Retention(RetentionPolicy.RUNTIME)//注解在哪个阶段执行
public @interface AccessLimit {
/**
* 在时间窗内的限流次数
* @return
*/
public int count() default 10;
/**
* 限流时间窗
* @return
*/
public int time() default 10;
public boolean pass() default false; // 超过限流次数是否也放行
}
创建Aop切面类
/**
* @title: AccessLimitAspect
* @description: 限流切面类
*/
@Aspect
@Component
@RequiredArgsConstructor
public class AccessLimitAspect {
private static final Logger logger = LoggerFactory.getLogger(AccessLimitAspect.class);
private final RedisTemplate redisTemplate;
@Before("@annotation(accessLimit)")
public void doBefore(JoinPoint joinPoint, AccessLimit accessLimit) throws Throwable {
int time = accessLimit.time();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
// 拼接redis key = IP + Api限流
String key = IpUtil.getIp(request) + request.getRequestURI();
// 获取redis的value
Integer maxTimes = null;
Object value = redisTemplate.opsForValue().get(key);
if (value != null) {
maxTimes = (Integer) value;
}
if (maxTimes == null) {
// 如果redis中没有该ip对应的时间则表示第一次调用,保存key到redis
redisTemplate.opsForValue().set(key, 1, time, TimeUnit.SECONDS);
} else if (maxTimes < accessLimit.count()) {
// 如果redis中的时间比注解上的时间小则表示可以允许访问,这是修改redis的value时间
redisTemplate.opsForValue().set(key, maxTimes + 1, time, TimeUnit.SECONDS);
} else {
// 请求过于频繁
logger.info("API请求限流拦截启动,{} 请求过于频繁", key);
throw new BusinessException("请求过于频繁,稍后重试");
}
}
}
使用注解
@AccessLimit
@GetMapping(value = "collect")
public ResponseResult collect(Integer articleId, HttpServletRequest request) {
return apiCollectService.collect(articleId,request);
}
在需要进行限流的方法上加上AcessLimit注解,当然后面也是可以配置参数的,我这里是用的默认,所以没有加,如果需要配置的话可以参考下面这个
@AccessLimit(count = 15,time = 20)
至此,方法限流也就完成了,只需简简单单的几步就可以完成对方法的限流,是不是很so easy~~呢