download:ChatGPT入门实战 时代更具竞争力的开发者
版本阐明
以下源码内容是基于RuoYi-Vue-3.8.2版本,即前后端别离版本
主要思想
标注了@RateLimiter注解的办法,在执行前调用lua脚本,把一段时间内的访问次数存入redis并返回,判别返回值能否大于设定的阈值,大于则抛出异常,由全局异常处置器处置
详细步骤
1. 注解
我们先来看一看@RateLimiter注解,在src/main/java/com/ruoyi/common/annotation包下
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter
{
// 限流key
public String key() default Constants.RATE_LIMIT_KEY;
// 限流时间,单位秒
public int time() default 60;
// 限流次数
public int count() default 100;
// 限流类型
public LimitType limitType() default LimitType.DEFAULT;
}
一个作用在办法上的注解,有四个属性
key:存储在redis里用到的key
time:限流时间,相当于redis里的有效期
count:限流次数
limitType: 限流类型,点开枚举发现有默许和IP两种限流方式,这两种方式的完成只是存储在redis里的key不同
2. 切面
我们来看一看@RateLimiter这个注解的切面RateLimiterAspect.java,在src/main/java/com/ruoyi/framework/aspectj包里
@Aspect
@Component
public class RateLimiterAspect
{
private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
private RedisTemplate<Object, Object> redisTemplate;
private RedisScript limitScript;
@Autowired
public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
{
this.redisTemplate = redisTemplate;
}
@Autowired
public void setLimitScript(RedisScript limitScript)
{
this.limitScript = limitScript;
}
@Before("@annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable
{
String key = rateLimiter.key();
int time = rateLimiter.time();
int count = rateLimiter.count();
String combineKey = getCombineKey(rateLimiter, point);
List keys = Collections.singletonList(combineKey);
try
{
// 调用lua脚本,传入三个参数
Long number = redisTemplate.execute(limitScript, keys, count, time);
if (StringUtils.isNull(number) || number.intValue() > count)
{
throw new ServiceException("访问过于频繁,请稍候再试");