站内搜索

搜索

06-02 13:26
05-31 17:11

自定义注解+拦截器实现访问限流

47

主题

38

点数

151

积分

地衡测影

积分
151

柴到了

发表于 2025-3-21 02:00:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x

自定义注解+拦截器实现访问限流

​ 无论是在项目中 , 还是面试时 , 都经常会遇到需要限流的问题 , 今天主要讲解通过自定义注解 + 拦截器实现限流的这一方式。

为什么要限流?

​ 现实生活中 , 某个店家给老年人发放鸡蛋 , 但是鸡蛋总数就那么多 , 如果有的人拿得多了 , 那么势必会出现一些问题 , 这样的结果就是部分人的体验不好 。限流的思想就是,控制每个人拿到鸡蛋的个数,拿完了就离开,保证每人都可以拿到三个鸡蛋。

​ 回到网络上,同样也是这个道理,例如系统中需要发送短信验证 , 但是某个用户恶意访问该接口 , 导致带宽和金钱白白消耗 , 那么就要需要想办法限制该用户的访问。

限流思路

对系统访问进行限流,我们采用自定义注解 + 拦截器实现 :

代码实现

首先我们需要自定义一个注解 , 用于需要限流的接口上 :

package com.alex.annotation;

import java.lang.annotation.*;

/**
 * @program: Blog
 * @description: 限流注解类
 * @author: Alex
 * @create: 2023-05-22 13:59
 **/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AccessLimit {
    int seconds();
    int maxCount();
}

其次 , 我们需要根据用户访问的ip进行限制 :

​ 获取ip及Redis相关工具 , 详见gitee仓库;

最后 , 我们在拦截器中进行拦截操作 :

package com.alex.interceptor;

import com.alex.annotation.AccessLimit;
import com.alex.service.RedisService;
import com.alex.util.IpUtil;
import com.alex.util.Result;
import com.alibaba.fastjson.JSON;
import io.lettuce.core.RedisConnectionException;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.OutputStream;

import static com.alex.constant.CommonConstant.APPLICATION_JSON;

/**
 * @program: Blog
 * @description: 访问限制拦截器
 * @author: Alex
 * @create: 2023-05-22 13:57
 **/
@Log4j2
@Component
@SuppressWarnings("all")
public class AccessLimitInterceptor implements HandlerInterceptor {

    @Resource
    private RedisService redisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 获取方法上的注解
            AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class);
            if (accessLimit != null) {
                long seconds = accessLimit.seconds();
                int maxCount = accessLimit.maxCount();
                String key = IpUtil.getIpAddress(request) + "-" + handlerMethod.getMethod().getName();

                try {
                    long q = redisService.incrExpire(key, seconds);
                    if (q > maxCount) {
                        render(response, Result.fail("请求过于频繁," + seconds + "秒后再试"));
                        log.warn(key + "请求次数超过每" + seconds + "秒" + maxCount + "次");
                        return false;
                    }
                    return true;
                } catch (RedisConnectionException e) {
                    log.warn("redis错误: " + e.getMessage());
                    return false;
                }
            }
        }
        return true;
    }

    private void render (HttpServletResponse response, Result < ?>result) throws IOException {
        response.setContentType(APPLICATION_JSON);
        OutputStream out = response.getOutputStream();
        String str = JSON.toJSONString(result);
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }
}

​ 以上 , 我们就可以通过自定义注解 + 拦截器 + Redis + 用户IP实现拦截功能 , 只需在需要限流的接口上加@AccessLimit注解 , 并且指定时间和最大访问次数。

温馨提示:看帖回帖是一种美德,您的每一次发帖、回帖都是对论坛最大的支持,谢谢! [这是默认签名,点我更换签名]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

加入社群

加入社群

Pixtech

Powered by Pixtech

© 2025 Pixtech Team.