SpringBoot使用自定义注解+AOP+Redis实现接口限流
为什么要限流
系统在设计的时候,我们会有一个系统的预估容量,长时间超过系统能承受的TPS/QPS阈值,系统有可能会被压垮,最终导致整个服务不可用。为了避免这种情况,我们就需要对接口请求进行限流。
所以,我们可以通过对并发访问请求进行限速或者一个时间窗口内的的请求数量进行限速来保护系统或避免不必要的资源浪费,一旦达到限制速率则可以拒绝服务、排队或等待。
限流背景
系统有一个获取手机短信验证码的接口,因为是开放接口,所以为了避免用户不断的发送请求获取验证码,防止恶意刷接口的情况发生,于是用最简单的计数器方式做了限流,限制每个IP每分钟只能请求一次,然后其他每个手机号的时间窗口限制则是通过业务逻辑进行判断。一般一些接口访问量比较大的,可能会压垮系统的,则需要加入流量限制!如:秒杀等...
实现限流
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、自定义限流注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter
{
/**
* 限流key
*/
String key() default Constants.RATE_LIMIT_KEY; /**
* 限流时间,单位秒
*/
int time() default 60; /**
* 限流次数
*/
int count() default 100; /**
* 限流类型
*/
LimitType limitType() default LimitType.DEFAULT; /**
* 限流后返回的文字
*/
String limitMsg() default "访问过于频繁,请稍候再试";
}
3、限流切面
@Aspect
@Component
public class RateLimiterAspect { private final static Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); @Autowired
private RedisUtils redisUtils; @Before("@annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable
{
int time = rateLimiter.time();
int count = rateLimiter.count();
long total = 1L; String combineKey = getCombineKey(rateLimiter, point);
try
{
if(redisUtils.hasKey(combineKey)){
total = redisUtils.incr(combineKey,1); //请求进来,对应的key加1
if(total > count)
throw new ServiceRuntimeException(rateLimiter.limitMsg());
}else{
redisUtils.set(combineKey,1,time); //初始化key
}
}
catch (ServiceRuntimeException e)
{
throw e;
}
catch (Exception e)
{
throw new ServiceRuntimeException("网络繁忙,请稍候再试");
}
} /**
* 获取限流key
* @param rateLimiter
* @param point
* @return
*/
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
{
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
if (rateLimiter.limitType() == LimitType.IP)
{
stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Class<?> targetClass = method.getDeclaringClass();
stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
return stringBuffer.toString();
} }
4、写一个简单的接口进行测试
@RestController
public class TestController { @RateLimiter(time = 60, count = 1, limitType = LimitType.IP, limitMsg = "一分钟内只能请求一次,请稍后重试")
@GetMapping("/hello")
public ResultMsg hello() {
return ResultMsg.success("Hello World!");
}
}
5、全局异常拦截
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); /**
* 业务异常
*/
@ExceptionHandler(ServiceRuntimeException.class)
public ResultMsg handleServiceException(ServiceRuntimeException e, HttpServletRequest request)
{
return ResultMsg.error(e.getMessage());
} /**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public ResultMsg handleException(Exception e, HttpServletRequest request)
{
return ResultMsg.error("系统异常");
} }
6、接口测试
1)第一次发送,正常返回结果

2)一分钟内第二次发送,返回错误,限流提示

好了,大功告成啦
还有其他的限流方式,如滑动窗口限流方式(比计数器更严谨)、令牌桶等...,有兴趣的小伙伴可以学习一下
附源码
https://gitee.com/jae_1995/ratelimiter
SpringBoot使用自定义注解+AOP+Redis实现接口限流的更多相关文章
- 服务限流 -- 自定义注解基于RateLimiter实现接口限流
1. 令牌桶限流算法 令牌桶会以一个恒定的速率向固定容量大小桶中放入令牌,当有浏览来时取走一个或者多个令牌,当发生高并发情况下拿到令牌的执行业务逻辑,没有获取到令牌的就会丢弃获取服务降级处理,提示一个 ...
- 库存秒杀问题-redis解决方案- 接口限流
<?php/** * Created by PhpStorm. * redis 销量超卖秒杀解决方案 * redis 文档:http://doc.redisfans.com/ * ab -n 1 ...
- spring中实现基于注解实现动态的接口限流防刷
本文将介绍在spring项目中自定义注解,借助redis实现接口的限流 自定义注解类 import java.lang.annotation.ElementType; import java.lang ...
- Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流
1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...
- 【高并发】亿级流量场景下如何为HTTP接口限流?看完我懂了!!
写在前面 在互联网应用中,高并发系统会面临一个重大的挑战,那就是大量流高并发访问,比如:天猫的双十一.京东618.秒杀.抢购促销等,这些都是典型的大流量高并发场景.关于秒杀,小伙伴们可以参见我的另一篇 ...
- SpringCloud微服务实战——搭建企业级开发框架(三十九):使用Redis分布式锁(Redisson)+自定义注解+AOP实现微服务重复请求控制
通常我们可以在前端通过防抖和节流来解决短时间内请求重复提交的问题,如果因网络问题.Nginx重试机制.微服务Feign重试机制或者用户故意绕过前端防抖和节流设置,直接频繁发起请求,都会导致系统防重 ...
- ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存
基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存 如何能更简洁的利用aop实现redis缓存,话不多说,上demo 需求: 数据查询时 ...
- Springboot+Redisson自定义注解一次解决重复提交问题(含源码)
前言 项目中经常会出现重复提交的问题,而接口幂等性也一直以来是做任何项目都要关注的疑难点,网上可以查到非常多的方案,我归纳了几点如下: 1).数据库层面,对责任字段设置唯一索引,这是最直接有效 ...
- 基于注解的接口限流+统一session认证
代码心得: 一个基本的做法:对于用户身份认证做到拦截器里,针对HandlerMethod进行统一拦截认证,根据方法上的注解标识,判别是否需要身份验证,并将查找出来的User实体存入ThreadLoca ...
随机推荐
- iNeuOS工业互联网操作系统,视图建模(WEB组态)增加2154个行业矢量图元、大屏背景及相关图元
1. 概述 现在三维数字孪生(3D)比较流行,各行业各领域的项目也都在上数字孪生项目或是项目中包括数字孪生模块,能做的厂家也很多.从全厂区的应用视觉的冲击力还是比较震撼,但是数字孪生不太可能包括 ...
- mysql-安装(windows版本)与登录
安装mysql 1.MySQL版本 mysql-5.6.35-winx64.zip 2.首先解压到安装目录 3.修改配置文件 复制my-default.ini 重命名为my.ini 然后修改mysql ...
- python采集一下美团外卖数据~~
所需知识点(https://jq.qq.com/?_wv=1027&k=Ap5XvyNN) 1.动态数据抓包演示2.json数据解析3.requests模块的使用4.保存csv 安装命令:re ...
- C#生成putty格式的ppk文件(支持passphrase)
背景 2022国家级护网行动即将开启,根据阿里云给出的安全建议,需要将登陆Linux的方式改为密钥对方式.我这里使用的远程工具是自己开发的,能够同时管理Windows和Linux,但是以前不支持密钥对 ...
- 动态树 — Link_Cut_Tree
[模板]动态树(Link Cut Tree) Link-cut-tree是一种维护动态森林的数据结构,在需要动态加边/删边的时候就需要LCT来维护. Link-cut-tree的核心是轻重链划分,每条 ...
- springboot中配置skywalking请求日志
pom.xml配置 <dependency> <groupId>org.apache.skywalking</groupId> <artifactId> ...
- Azure Devops(十五) 使用Azure的私有Maven仓库
上一篇文章中,我们介绍了如何使用Azure的nuget仓库,今天我们来研究一下如何使用azure给我们提供的maven仓库. 首先,我们打开azureDevops,点击到制品界面,然后选择maven. ...
- 使用Python3.7+Tornado5.1配合七牛云存储api来异步切分上传文件
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_123 之前写了几篇关于FastDfs分布式存储的文章:python3.7.3操作FastDfs来进行文件操作,其实市面上关于云存储 ...
- 轻盈潇洒卓然不群,敏捷编辑器Sublime text 4中文配置Python3开发运行代码环境(Win11+M1 mac)
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_210 20世纪初,几乎所有的飞机都是并列双翼结构,此时,美国著名飞行大亨霍华德·休斯认为自己的飞机不够快,助手委婉地提醒他,如果速 ...
- JPA作持久层操作
JPA(Hibernate是jpa的实现) jpa是对实体类操作,从而通过封装好的接口直接设置数据库的表结构.虽然jpa可以直接通过编写java代码来操作数据库表结构,避免了sql的编写,但别忘了需要 ...