Spring AOP实战开发(四)AOP+Redis实现接口限流

需求:要求同一个接口同一时间 仅能处理N个请求,超过就返回;不是时间窗口也不是秒杀,接口处理完成后又可以继续接收请求;

分析:有点像令牌桶算法,但是并不是每隔一段时间放几个令牌,而是永远只有这几个令牌;计划采用redis的increase方式,设定一个最大值Max,每次请求先判定当前key是否超过Max,如果已经超过就返回,没有超过就自增,分布式可用;

实现:

  创建一个注解类AccessLimit ,key是需要需要自增的redis中的key,可以由接口提供,limit就是最大值

@Inherited
@Documented
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {

    /**
     * 标识 指定sec时间段内的访问次数限制
     */
    String key() default "default";

    /**
     * 标识 指定sec时间段内的访问次数限制
     */
    int limit() default 5;

    /**
     * 标识 时间段
     **/
    int sec() default 5;
}

  controller中写法如下,#type 表示获取参数名称为type的值

    @AccessLimit(key = "#type")
    @PostMapping("/single")
    public CloudwalkResult<String> uploadAttach(@RequestParam("file") MultipartFile file,
                                                @RequestParam(value = "type", required = false, defaultValue = "default") String type) {
        // 附件的访问地址
        return uploadService.uploadSingleFile(file, type);
    }

 

 对应的切面类如下

@Aspect
@Component
public class AccessLimitAspect {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private CacheHandler cacheHandler;

    /**
     * 这里我们使用注解的形式
     * 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
     */
    @Pointcut("@annotation(cn.cloudwalk.developer.center.web.common.aspect.AccessLimit)")
    public void logPointCut() {
    }

    /**
     * 环绕通知 @Around  , 当然也可以使用 @Before (前置通知)  @After (后置通知)
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        // 获取注解的方法参数列表
        Object[] args = pjp.getArgs();

        // 获取被注解的方法
        MethodInvocationProceedingJoinPoint mjp = (MethodInvocationProceedingJoinPoint) pjp;
        MethodSignature signature = (MethodSignature) mjp.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = signature.getParameterNames();

        // 获取方法上的注解
        try {
            AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
            if (accessLimit == null) {
                // 如果没有注解
                return pjp.proceed();
            }
            int limit = accessLimit.limit();
            String key = accessLimit.key();
            if (key.startsWith("#")) {
                key = key.replaceFirst("#", "");
            }
            String keyTemp = key;
            // 获取方法上的key的值
            try {
                for (int i = 0; i < parameterNames.length; i++) {
                    if (parameterNames[i].equals(key)) {
                        key = (String) args[i];
                        break;
                    }
                }
            } catch (Exception e) {
                key = keyTemp;
            }
            if (!cacheHandler.limitForRequest(key, limit)) {
                return Result.fail(ServiceCodeConstants.UP_IS_ERROR, "同一时间上传请求过多,请稍后再试");
            }
        } catch (Exception e) {
            logger.error("AccessLimit 切面方法失败", e);
        }
        return pjp.proceed();
    }
}

  这里仅是加+ ,需要在业务代码中减一,如下 核心代码 cacheHandler.releaseForRequest(type);

    @Override
    public CloudwalkResult<String> uploadSingleFile(MultipartFile file, String type) {
        String imgurl = "";
        String fileUploadPath = IMAGES_PATH + type;
        // 上传
        try {
            ...
        } catch (IOException e) {
            logger.error("附件访问地址获取异常,原因:", e);
        } finally {
            cacheHandler.releaseForRequest(type);
        }
        return CloudwalkResult.success(imgurl);
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值