当前位置: 首页 > news >正文

SpringBoot限制(限流)接口访问频率

限流整个流程过程

1.首先用户的请求进来,将用户ip和uri组成key,timestamp为value,放入zset
2. 更新当前key的缓存过期时间,这一步主要是为了定期清理掉冷数据,和上面我提到的常见错误设计2中的意义不同
3. 删除窗口之外的数据记录
4. 统计当前窗口中的总记录数
5. 如果记录数大于阈值,则直接返回错误,否则正常处理用户请求

首先是定义一个注解,方便后续对不同接口使用不同的限制频率

package org.jeecg.common.aspect.annotation;import java.lang.annotation.*;/*** @Author xu* @create 2023/8/2 19*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {// 限制时间 单位:秒(默认值:一分钟)long period() default 60;// 允许请求的次数(默认值:5次)long count() default 5;}

切面AOP处理逻辑

package org.jeecg.common.aspect;import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.jeecg.common.aspect.annotation.RequestLimit;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;/*** @Author xu* @create 2023/8/2 19*/
@Aspect
@Component
@Log4j2
public class RequestLimitAspect {@AutowiredRedisTemplate redisTemplate;// 切点@Pointcut("@annotation(requestLimit)")public void controllerAspect(RequestLimit requestLimit) {}@Around("controllerAspect(requestLimit)")public Object doAround(ProceedingJoinPoint joinPoint, RequestLimit requestLimit) throws Throwable {long period = requestLimit.period();long limitCount = requestLimit.count();Object[] args = joinPoint.getArgs();String ip = null;String url = null;for (Object arg : args) {if (arg instanceof HttpServletRequest) {HttpServletRequest request = (HttpServletRequest) arg;ip = request.getRemoteAddr();url = request.getRequestURI();break;  // 如果找到了符合条件的参数,可以选择跳出循环}}String key = "req_limit_".concat(url).concat(ip);ZSetOperations zSetOperations = redisTemplate.opsForZSet();long currentMs = System.currentTimeMillis();zSetOperations.add(key, currentMs, currentMs);redisTemplate.expire(key, period, TimeUnit.SECONDS);zSetOperations.removeRangeByScore(key, 0, currentMs - period * 1000);Long count = zSetOperations.zCard(key);if (count > limitCount) {log.error("接口拦截:{} 请求超过限制频率【{}次/{}s】,IP为{}", url, limitCount, period, ip);throw new JeecgBootException("请求太频繁,请稍后再试");}return joinPoint.proceed();}}

Controller层使用

	@AutoLog(value = "访客数据-添加")@RequestLimit(count = 2,period = 20)@ApiOperation(value="访客数据-添加", notes="访客数据-添加")@PostMapping(value = "/verifySave")public Result<?> verifySave(@RequestBody SysVisitantData sysVisitantData,HttpServletRequest request) {String ip = request.getRemoteAddr();String url = request.getRequestURI();return Result.OK("添加成功!");}
http://www.lryc.cn/news/106955.html

相关文章:

  • 蓝桥杯,我劝你不要参加的8个完美理由
  • ChatGPT及其工作原理;OpenAI申请注册商标GPT-5,引发关注
  • [C++项目] Boost文档 站内搜索引擎(2): 文档文本解析模块parser的实现、如何对文档文件去标签、如何获取文档标题...
  • 若依框架vue使用Element 如何把当前页面的所有Table表格row.id和一个表单的16个字段内容通过js传Java后台,Java后台是如何接收的
  • 迁移学习:使用Restnet预训练模型构建高效的水果识别模型
  • 浅谈机器视觉
  • 助力保险行业数字化创新,麒麟信安参展2023中国财险科技应用高峰论坛
  • eclipse was unable to locate its companion shared library
  • 【MySQL】使用C/C++连接MySQL数据库
  • 【Python】从同步到异步多核:测试桩性能优化,加速应用的开发和验证
  • 使用checkBox组件时,动态设置disabled,仍能触发click事件的原因及解决办法
  • 【JavaScript】如何进行除法运算且保留小数部分不参与四舍五入【推荐库bignumber.js 】
  • 掌握Java JDK 1.8 API帮助文档中文版,事半功倍编程
  • Spring Boot的自动配置原理
  • NFS服务器
  • 说明学习委员之作业管理系统—后端部分
  • 质数(判定质数 分解质因数 筛质数)
  • SAP数据库表维护视图生成器的使用
  • 数据结构 | 递归
  • 微信发视频怎么不压缩画质?试试这几招
  • 【网络安全带你练爬虫-100练】第16练:使用session发送请求
  • 论文代码学习—HiFi-GAN(3)——模型损失函数loss解析
  • CLion中avcodec_receive_frame()问题
  • Linux安装操作(Mac版本)
  • Linux(四)--包软件管理器与Linux上软件的下载示例
  • HTML <param> 标签
  • 基于ARM+FPGA (STM32+ Cyclone 4)的滚动轴承状态监测系统
  • 二、数据结构10:堆 模板题+算法模板(堆排序,模拟堆)
  • W6100-EVB-PICO做DNS Client进行域名解析
  • 【linux-网络】4层转发方法-iptable以及nginx