pom.xml引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
逻辑代码
private boolean limitIpCallThis(String key) {
Long increment = stringRedisTemplate.opsForValue().increment(key, 1);
switch (increment.intValue()) {
case 1:
// 第一次调用,初始化过期时间60分钟
stringRedisTemplate.expire(key, 60, TimeUnit.MINUTES);
break;
case 5:
// 达到阈值后,刷新过期时间为5分钟
stringRedisTemplate.expire(key, 5, TimeUnit.MINUTES);
break;
default:
break;
}
return increment > 5;
}
由请求方IP组成Redis的key,请求次数自增1。达到阈值5,即可通过此方法返回的true来提示请求者操作频繁
String.format("您的操作过于频繁,请%s分钟后再试! ", stringRedisTemplate.getExpire(key, TimeUnit.MINUTES))
这段代码可以放入Controller层的入口,也可结合AOP切面编程,给接口做调用频率限制。
AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
自定义注解
/**
* 接口调用次数限制
*/
@Target(ElementType.METHOD) // 作用于方法
@Retention(RetentionPolicy.RUNTIME) // 生命周期
public @interface Limit {
/**
* 阈值,达到阈值拒绝请求
*/
long threshold() default 0;
/**
* 重置时间,重置时间结束方解除限制
*/
long reset() default 0;
/**
* 重置时间单位,默认分钟,可自定义设置
*/
TimeUnit unit() default MINUTES;
}
切面逻辑
@Around("@annotation(xxxx.annotation.Limit)")
public Object limit(ProceedingJoinPoint joinPoint) throws Throwable {
// 取出注解实例
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Limit limit = method.getAnnotation(Limit.class);
// TODO 根据接口Limit实例的阈值和重置时间做对应处理 (改动上文的limitIpCallThis方法,当该方法返回true抛出异常即可)
...
return joinPoint.proceed();
}
使用示例