使用redis 进行请求限流

java_使用redis 进行请求限流

应用场景:在后端处理流程复杂,前端可能会高频点击的情况下,做请求限流来进行系统保护;

本文的应用场景为:前端请求导出excel,出现大数据量的情况下,限制每个ip 用户对于每个地区条件,一分钟内只能请求两次

思路:使用注解在对要限流的方法进行标识,自定义拦截器,在进入这个方法体之前,通过redis 获取缓存的key,该key 设置一个超时时间,该key 的value 为访问次数,每访问一次便+1,达到次数后则不进入方法体,直接返回请求已满,直到key 过期;

代码实现:

0.配置信息:

pom.xml :

<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-data-redis</artifactId>  <version>2.5.7</version></dependency>

1, 自定义注解 ======================================================

@Retention(RUNTIME)//运行时有效

@Target(ElementType.METHOD)//用在方法上

public @interface AccessLimit {

   //时间范围(单位:秒)

   int seconds();

   //在这个时间范围内最大访问次数

   int maxCount();

}

2.自定义拦截器============================================================

@Slf4j

@Component

public class ExportExcelLimitInterceptor implements HandlerInterceptor {

   @Autowired

   private RedisTemplate redisTemplate;

   @Override

   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

       if (handler instanceof HandlerMethod) {

           HandlerMethod hm = (HandlerMethod) handler;

           //设置redisTemplate的序列化方式(必须设置为这种方式,因为要用到incr)

           redisTemplate.setKeySerializer(new StringRedisSerializer());

           redisTemplate.setValueSerializer(new StringRedisSerializer());

           AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);

           if (null == accessLimit) {

               return true;

           }

           int seconds = accessLimit.seconds();

           int maxCount = accessLimit.maxCount();

           String ipAddr = IpUtil.getIpAddr(request);

           String key = request.getContextPath() + ":" + request.getServletPath() + "_exportExcel"+":" + ipAddr ;

           String countStr = (String) redisTemplate.opsForValue().get(key);

           Integer count = null;

           //如果不是第一次访问,则把访问次数转换为integer类型

           if(countStr != null){

               count = Integer.valueOf(redisTemplate.opsForValue().get(key).toString());

           }

           log.info("count:{}",count);

           //拿到访问次数的过期时间

           Long keySeconds = redisTemplate.getExpire(key);

           if (null == count || -1 == count) {

               redisTemplate.opsForValue().set(key,String.valueOf(1));

               //设置过期时间

               redisTemplate.expire(key,seconds, TimeUnit.SECONDS);

               return true;

           }

           if (count < maxCount) {

               log.info("count:{}",count);

               //访问次数+1

               redisTemplate.opsForValue().increment(key,1);

               //设置剩余过期时间(修改完该key的value值后,对应的过期时间会失效,需重新设置)

               redisTemplate.expire(key,keySeconds, TimeUnit.SECONDS);

               return true;

           }

           if (count >= maxCount) {

//                response 返回 json 请求过于频繁请稍后再试

               this.responseResult(response, new Result(500, "访问次数已超过限制,请稍后重试"));

               return false;

           }

       }

       return true;

   }

   void responseResult(HttpServletResponse response, Result result) throws IOException {

       response.setHeader("Content-Type", "application/json;charset=utf8");

       Writer writer = response.getWriter();

       writer.write(JSONObject.toJSONString(result));

       writer.close();

   }

}

3.添加拦截器到webconfig ===============================================================================

@Configuration

public class WebMvcConfig implements WebMvcConfigurer {

   @Autowired

   private ExportExcelLimitInterceptor excelLimitInterceptor; // 访问限制拦截器


   @Override

   public void addInterceptors(InterceptorRegistry registry) {

       registry.addInterceptor(excelLimitInterceptor)

               .addPathPatterns("/qiannan/test");

   }

}

4.添加注解到方法体===================================================================================

@AccessLimit(seconds=30,maxCount=2)

@GetMapping("/qiannan/test")

public Object test(String id) throws IOException {

   return linkFeignClient.detail(id);

}

5. 用到的工具类:

public class IpUtil {

   private IpUtil() { }

   /**

    * 获取登录用户的IP地址

    * @param request

    * @return

    */

   public static String getIpAddr(HttpServletRequest request) {

       String ip = request.getHeader("x-forwarded-for");

       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {

           ip = request.getHeader("Proxy-Client-IP");

       }

       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {

           ip = request.getHeader("WL-Proxy-Client-IP");

       }

       if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {

           ip = request.getRemoteAddr();

       }

       if (ip.equals("0:0:0:0:0:0:0:1")) {

           ip = "127.0.0.1";

       }

       if (ip.split(",").length > 1) {

           ip = ip.split(",")[0];

       }

       return ip;

   }

   //ip转化为有序的长整形

   public static long getIp2long(String ip) {

       ip = ip.trim();

       String[] ips = ip.split("\\.");

       long ip2long = 0L;

       for (int i = 0; i < 4; ++i) {

           ip2long = ip2long << 8 | Integer.parseInt(ips[i]);

       }

       return ip2long;

   }

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容