请求限流

  1. 后端接口限流
    1. 初步方案
    2. 增强方案

后端接口限流

如果有人使用token+postman多次调用服务端某个接口,那么会造成服务繁忙,因此我们需要设置来自同一用户的请求时间间隔

使用AOP+redis进行请求冷却设置

设计思路:

  • 使用AOP+自定义注解对需要限流的服务进行切面配置

  • 在切面中获得当前请求的用户id

  • 将用户id作为key存入redis缓存中,value可以不设置,通过读取注解上的请求限流时间来设置key的失效时间

  • 下一次请求被AOP拦截到则进行一次获取key的操作,如果key失效不存在则服务器受理请求,否则直接返回拒绝服务

初步方案

配置切面

@Component
@Aspect
public class ApiAspect {

    @Autowired
    private RedisCache redisCache;

    @Pointcut("@annotation(com.os467.annotation.ApiLimit)")
    public void pt(){

    }

    @Around("pt()")
    public Object limitRequest(ProceedingJoinPoint joinPoint) throws Throwable {

        Long userId = SecurityUtils.getUserId();

        if (redisCache.getCacheObject("apiLimit:" + userId) == null) {
            //记录正在发生请求
            redisCache.setCacheObject("apiLimit:" + userId, "");

            //获取当前增强方法的标签信息
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            //获取超时时间
            int timeout = method.getAnnotation(ApiLimit.class).timeout();

            redisCache.expire("apiLimit:" + userId,timeout, TimeUnit.SECONDS);
        } else {
            //上一个请求还未结束,拒绝服务
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "请勿重复操作");
        }

        //增强方法的返回值
        Object ret = null;
        try {
            ret = joinPoint.proceed();
        } catch (Throwable throwable) {
            throw throwable;
        }

        return ret;
    }

}

此方案可以初步排除更新操作的重复操作,但是在添加操作中失效了

分析原因可能是因为多个线程同时判断冷却标记为null从而导致AOP拦截失效

增强方案

解决方案

使用双重if判断+线程同步代码块的方案 来保证只有一个请求被受理

//第一次检查缓存中是否有冷却标记
if (redisCache.getCacheObject("apiLimit:"+userId) != null){
    //上一个请求还未结束,拒绝服务
    return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "请勿重复操作");
}else {
    //静态代码块,持有当前切面类的对象锁
    synchronized (this) {
        if (redisCache.getCacheObject("apiLimit:" + userId) == null) {

            //记录冷却标记
            redisCache.setCacheObject("apiLimit:" + userId, "");

            //获取当前增强方法的标签信息
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            //获取超时时间
            int timeout = method.getAnnotation(ApiLimit.class).timeout();

            //设置冷却标记失效时间
            redisCache.expire("apiLimit:" + userId,timeout, TimeUnit.SECONDS);
        } else {
            //上一个请求还未结束,拒绝服务
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "请勿重复操作");
        }
    }
}

此时对用户A的多次重复请求已经做了冷却限制,保证服务器在5秒内只受理一次用户A的请求

此时有一个问题

当前线程同步代码块使用的是AOP监视器的对象锁,因此思考是否会对其它线程请求造成阻塞影响?

  • 答案显然是不会的,经过测试AOP监视器是多例模式的,因此不会因为同步代码块造成不同请求线程之间的阻塞

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com

文章标题:请求限流

字数:779

本文作者:Os467

发布时间:2022-10-08, 19:57:03

最后更新:2022-10-08, 19:57:07

原始链接:https://os467.github.io/2022/10/08/%E8%AF%B7%E6%B1%82%E9%99%90%E6%B5%81/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏