自定义注解+redis实现简单接口限流

文章介绍了如何通过创建一个自定义注解`@RequestLimit`来限制接口的请求频率,注解可以在方法或类级别应用,并在运行时生效。在拦截器中检查注解并根据注解限制请求次数,利用Redis存储请求计数,超过限制则阻止请求并返回错误信息。
摘要由CSDN通过智能技术生成

1.自定义注解

@Documented
@Inherited
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {
    // 在 second 秒内,最大只能请求 maxCount 次
    int second() default 1;
    int maxCount() default 5;
}
  • @Target({ElementType.METHOD,ElementType.TYPE}) 指定这个注解可以用在方法或者类上(或者接口)
  • @Retention(RetentionPolicy.RUNTIME) 指定注解的生命周期是运行时。
    首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。

2.使用注解

直接在controller上加注解即可,当然也可以在单独接口方法加,这里表示1秒最多三次请求,如果不指定,则按照默认1秒五次(定义注解时指定的默认值)
在这里插入图片描述

3.拦截器

想要注解生效需要定义接口访问的拦截器,访问接口前先通过拦截器获取到我们的自定义注解及配置内容,实现限流

@Component
public class RequestLimitIntercept implements HandlerInterceptor {
    @Autowired
    RedisTemplate<String,Object> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * isAssignableFrom() 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
         * isAssignableFrom()方法是判断是否为某个类的父类
         * instanceof关键字是判断是否某个类的子类
         */
        if(handler.getClass().isAssignableFrom(HandlerMethod.class)){
            //HandlerMethod 封装方法定义相关的信息,如类,方法,参数等
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            // 获取方法中是否包含注解
            RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
            //获取 类中是否包含注解,也就是controller 是否有注解
            RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
            // 如果 方法上有注解就优先选择方法上的参数,否则类上的参数
            RequestLimit requestLimit = methodAnnotation != null?methodAnnotation:classAnnotation;
            if(requestLimit != null){
                if(isLimit(request,requestLimit)){
                    responseOut(response, R.error(403,"请求过快"));
                    //throw new RuntimeException("请求过快");
                    return false;
                }
            }
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
    //判断请求是否受限
    public boolean isLimit(HttpServletRequest request,RequestLimit requestLimit){
        // 受限的redis 缓存key ,因为这里用浏览器做测试,我就用sessionid 来做唯一key,如果是app ,可以使用 用户ID 之类的唯一标识。
        String limitKey = request.getServletPath()+request.getSession().getId();
        // 从缓存中获取,当前这个请求访问了几次
        Integer redisCount = (Integer) redisTemplate.opsForValue().get(limitKey);
        if(redisCount == null){
            //初始 次数
            redisTemplate.opsForValue().set(limitKey,1,requestLimit.second(), TimeUnit.SECONDS);
            System.out.println("写入redis --");
        }else{
            System.out.println("intValue-->"+redisCount.intValue());
            if(redisCount.intValue() >= requestLimit.maxCount()){
                return true;
            }
            // 次数自增
            redisTemplate.opsForValue().increment(limitKey);
        }
        return false;
    }
    /**
     * 回写给客户端
     * @param response
     * @param result
     * @throws IOException
     */
    private void responseOut(HttpServletResponse response, R result) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = null ;
        String json = JSONObject.toJSON(result).toString();
        out = response.getWriter();
        out.append(json);
    }

}

流程:请求进来先进拦截器,然后获取到接口方法,如果是处理请求的方法就获取到当前方法和当前方法所在的类是否有RequestLimit 注解(方法上的注解优先),如果有的话判断是否达到限制,达到的话进行拦截,返回限流信息。
判断是否达到限流的流程:获取sessionid作为redis的key,拿到请求次数,如果次数为空,就保存到redis,value是1,过期时间是注解指定的判断时间(这里是1s),如果在1s内请求再次进来,redis中的value就进行+1操作,如果redis中value的值高于注解设定的值,则表示请求次数达到限制,返回true,否则返回false。
这样一个简单的接口限流就实现了。
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值