👨🎓作者:bug菌
✏️博客:CSDN、掘金、infoQ、51CTO等
🎉简介:CSDN|阿里云|华为云|51CTO等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,掘金 | InfoQ | 51CTO等社区优质创作者,全网粉丝合计15w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。...
✍️温馨提醒:本文字数:1999字, 阅读完需:约 5 分钟
🏆本文收录于《Spring Boot从入门到精通》,专门攻坚指数提升。
本专栏致力打造最硬核 Spring Boot 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。
环境说明:Windows10 + Idea2021.3.2 + Jdk1.8 + SpringBoot 2.3.1.RELEASE
1. 前言
在日常业务开发中,处理重复请求应该是我们需要经常注意的,在某些情况下是可能重复发送的,如果是查询类操作并无大碍,但其中有些请求是涉及写入操作的,一旦重复了,很可能会导致很严重的后果,例如交易的接口如果重复请求就可能会重复下单。还比如如下场景:
那么在Spring Boot 中,防止重复请求的方法有那些?像如何禁止用户重复点击等客户端操作将不在本文的讨论范畴(有点low),我要玩点高级的,同学们请看:
- Token 验证
解析: 在页面中生成一个唯一的Token,然后在请求中携带此Token,服务端接收到请求后验证解析该Token是否是正确的。如果Token不正确,则认为是重复请求并过滤/拒绝该次请求。
- Token 桶算法
解析: 在服务端中使用Token桶算法对请求进行限制,每个用户都有一个Token桶,每次请求需要从Token桶中获取一个Token,如果Token桶中没有用户此次请求的Token,则认为是重复请求并过滤/拒绝该次请求。
- 限流控制
解析: 通过在请求接口中设置一个时间间隔,例如5秒钟,同一个用户在5秒钟内只能请求一次,如果再次请求则认为是重复请求并过滤/拒绝该次请求。
- 接口密等性设计
解析: 通过设计接口的幂等性来防止重复请求。在设计接口时,确保同样的请求不管发送多少次都会得到相同的结果,这样即使用户发送了重复请求,服务端都返回同一种请求结果,也就不会对系统及数据造成影响。
那么,具体如何一一实现呢?这将又会是干货满满的一期,全程无尿点不废话只抓重点教,具有非常好的学习效果,拿好小板凳准备就坐!希望学习的过程中大家认真听好好学,学习的途中有任何不清楚或疑问的地方皆可评论区留言或私信,bug菌将第一时间给予解惑,那么废话不多说,直接开整!Fighting!!
2. 环境说明
本地的开发环境:
- 开发工具:IDEA 2021.3
- JDK版本: JDK 1.8
- Spring Boot版本:2.3.1 RELEASE
- Maven版本:3.8.2
3. 正文
那么接下来,我就给同学们逐一具体介绍下如上四种防止重复请求的解决方案吧。
3.1 Token验证
3.1.1 算法思路
在页面中生成一个唯一的Token,然后在请求中携带此Token,服务端接收到请求后验证解析该Token是否是正确的。如果Token不正确,则认为是重复请求并过滤/拒绝该次请求。
3.1.2 代码实现
①首先在页面中生成一个唯一的Token
提交
②在服务端中接收请求并验证Token
@RestController
public class DemoController{
private Map tokenMap = new ConcurrentHashMap();
@PostMapping("/submit")
public String submit(HttpServletRequest request) {
//获取请求携带的参数token值
String token = request.getParameter("token");
//校验是否存有
if (!tokenMap.containsKey(token)) {
//不存在则放过,存在则判定同一次请求
tokenMap.put(token, true);
// 处理请求
return "success";
} else {
// 重复请求
return "error";
}
}
}
3.1.3 代码解析
在上述代码中,DemoController
是一个基于Spring的RESTful API控制器类,其中包含了一个用于防止重复提交的功能。具体来说,它针对来自客户端的POST请求,从请求参数中获取一个名为"token"的值,用于判断这个请求是否已经被处理过。然后,它使用一个ConcurrentHashMap
来保存已经处理过的请求,以便在后续的请求中进行比对和校验。如果请求中的token在map中不存在,则将其添加到map中,并处理请求。如果已经存在,则说明请求是重复的,会返回错误信息。
3.2 Token桶算法
3.2.1 算法思路
在服务端中使用Token桶算法对请求进行限制,每个用户都有一个Token桶,每次请求需要从Token桶中获取一个Token,如果Token桶中没有用户此次请求的Token,则认为是重复请求并过滤/拒绝该次请求。
3.2.2 代码实现
@RestController
public class DemoController {
private Map tokenBucketMap = new ConcurrentHashMap();
@PostMapping("/submit")
public String submit(HttpServletRequest request) {
//获取用户id
String userId = request.getParameter("userId");
//通过用户id生成一个token桶
LinkedList tokenBucket = tokenBucketMap.get(userId);
if (tokenBucket == null) {
tokenBucket = new LinkedList();
tokenBucketMap.put(userId, tokenBucket);
}
long currentTime = System.currentTimeMillis();
synchronized (tokenBucket) {
if (tokenBucket.size() 60000) {
tokenBucket.addLast(currentTime);
if (tokenBucket.size() > 10) {
tokenBucket.removeFirst();
}
// 处理请求
return "success";
} else {
// 重复请求
return "error";
}
}
}
}
3.2.3 代码解析
这是一个基于Spring框架的RESTful API
的示例代码,使用了令牌桶算法对请求进行限流。具体来说,代码中实现了一个名为submit
的POST请求处理函数,能够获取HTTP请求中的参数(userId),并将该用户对应的令牌桶(tokenBucket)放入一个Map
(tokenBucketMap
)中进行管理。如果该用户对应的令牌桶不存在,代码会创建一个新的令牌桶并将其放入Map中进行管理。
接下来,代码对令牌桶进行操作。如果令牌桶中当前令牌数量小于10个,或者距离令牌桶中第一个令牌的时间超过了60秒,那么当前请求可以被处理。此时代码会在令牌桶的尾部添加一个新的令牌,并将该请求标记为成功处理。如果令牌桶中当前令牌数量已经达到了10个,那么代码会移除令牌桶中的第一个令牌,再将新的令牌添加到令牌桶的尾部,从而保证令牌桶中始终只有10个令牌。
如果当前请求不符合上述条件,代码会直接返回错误信息,标记该请求为重复请求。为了保证多线程并发处理时令牌桶的安全性,代码使用了synchronized关键字对令牌桶进行了加锁。乐观情况下,该令牌桶算法能够有效地限流用户的请求,防止接口被恶意攻击或者异常请求所耗尽。
3.3 限流控制
3.3.1 算法思路
通过在请求接口中设置一个时间间隔,例如5秒钟,同一个用户在5秒钟内只能请求一次,如果再次请求则认为是重复请求并过滤/拒绝该次请求。
3.3.2 代码实现
@RestController
public class DemoController {
private Map lastRequestTimeMap = new ConcurrentHashMap();
@PostMapping("/submit")
public String submit(HttpServletRequest request) {
//获取请求中的用户id
String userId = request.getParameter("userId");
//从map中查找上次请求的时间戳
Long lastRequestTime = lastRequestTimeMap.get(userId);
//如果为空或者上次请求的时间戳与当前时间做差大于5s,则视为新请求,否则重复请求。
if (lastRequestTime == null || System.currentTimeMillis() - lastRequestTime > 5000) {
lastRequestTimeMap.put(userId, System.currentTimeMillis());
// 处理请求
return "success";
} else {
// 重复请求
return "error";
}
}
}
3.3.3 代码解析
在Controller
中定义了一个Map
对象lastRequestTimeMap
,用于存储每个用户的最近一次请求时间戳。
在submit
方法中,通过HttpServletRequest
获取请求中的用户id,然后从lastRequestTimeMap中查找该用户的最近一次请求时间戳。
如果lastRequestTime
为空,即用户第一次请求,或者当前时间与上次请求时间戳间隔大于5秒,则认为是新请求,将当前时间戳放入lastRequestTimeMap
并返回"success"。
如果当前时间与上次请求时间戳间隔小于等于5秒,则认为是重复请求,直接返回"error"。
总而言之,目的是为了防止频繁重复的请求对后端系统造成过大的负载,通过控制重复请求的处理,可以有效节省服务器资源。
3.4 接口密等性设计
3.4.1 思路分析
通过设计接口的幂等性来防止重复请求。在设计接口时,确保同样的请求不管发送多少次都会得到相同的结果,这样即使用户发送了重复请求,服务端都返回同一种请求结果,也就不会对系统及数据造成影响。
3.4.2 代码实现
@RestController
public class DemoController {
private Map resultCache = new ConcurrentHashMap();
@PostMapping("/submit")
public String submit(HttpServletRequest request) {
//指定一个请求参数key。
String key = request.getParameter("key");
//将请求结果存放到key值里
String result = resultCache.get(key);
//如果result不为空则说明是重复请求。
if (result != null) {
// 返回之前的结果
return result;
} else {
// 处理请求并缓存结果
// 模拟一个结果赋值
result = doBusinessLogic();
// 将结果缓存
resultCache.put(key, result);
// 返回
return result;
}
}
private String doBusinessLogic() {
// 业务逻辑处理
return "success";
}
}
3.4.3 代码解析
这是一个基于Spring框架的RESTful Web Service
,其中包含一个使用了缓存的HTTP POST
请求处理方法。代码实现步骤具体如下:
@RestController
注解指明这是一个RESTful Web Service
的Controller
类。
resultCache
是一个线程安全的并发HashMap
,用于缓存请求的处理结果,key为请求参数中的某个值。
submit
方法是HTTP POST
请求的处理方法,它接受一个HttpServletRequest
对象作为参数。在方法中,首先解析出请求参数中的key值。
然后从resultCache
中根据key值取出对应的处理结果,如果能取到结果,则直接返回之前的结果。
如果没有取到结果,则执行业务处理逻辑(doBusinessLogic
方法),然后将处理结果放入resultCache
中进行缓存,并返回处理结果。
doBusinessLogic
方法是一个示例性质的业务处理逻辑,这里直接返回了字符串"success"。
总体上,这个HTTP POST
请求处理方法是使用了缓存机制的,通过缓存处理结果可以减少重复计算和响应时间,提高响应效率。
... ...
好啦,以上就是4种解决重复请求的方式啦,同学们觉得哪种更好可以在文末进行投票讨论交流哦。
以上就是我这期的全部内容啦,如果还想学习更多,你可以看看如下的往期热文推荐哦,每天积累一个奇淫小知识,日积月累下去,你一定能成为令人敬仰的大佬。
「赠人玫瑰,手留余香」,咱们下期拜拜~~
4. 热文推荐
若想学习更多,可以参考这篇专栏总结《2023最新首发,全网最全 Spring Boot 学习宝典(附思维导图)》,本专栏致力打造最硬核 Spring Boot 进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中。欢迎大家订阅持续学习。
在入门及进阶之途,我必助你一臂之力,系统性学习,从入门到精通,带你不走弯路,直奔终点;投资自己,永远性价比最高,都这么说了,你还不赶紧来学??
5. 文末
我是bug菌,CSDN | 阿里云 | 华为云 | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,掘金 | InfoQ | 51CTO等社区优质创作者,全网粉丝合计15w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。