精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

SpringBoot自定義注解+AOP+redis實現防接口冪等性重復提交,從概念到實戰

開發 前端
本次解決是對于高并發不高的情況,適用于一般的管理系統,給出的解決方案!!高并發的還是建議加分布式鎖!

一、前言

在面試中,經常會有一道經典面試題,那就是:怎么防止接口重復提交?小編也是背過的,好幾種方式,但是一直沒有實戰過,做多了管理系統,發現這個事情真的沒有過多的重視。最近在測試過程中,發現了多次提交會保存兩條數據,進而導致程序出現問題!

問題已經出現我們就解決一下吧!!

本次解決是對于高并發不高的情況,適用于一般的管理系統,給出的解決方案!!高并發的還是建議加分布式鎖!!

下面我們來聊聊冪等性是什么?

二、什么是冪等性

接口冪等性就是用戶對于同一操作發起的一次請求或者多次請求的結果是一致的,不會因為多次點擊而產生了副作用;比如說經典的支付場景:用戶購買了商品支付扣款成功,但是返回結果的時候網絡異常,此時錢已經扣了,用戶再次點擊按鈕,此時會進行第二次扣款,返回結果成功,用戶查詢余額返發現多扣錢了,流水記錄也變成了條,這就沒有保證接口的冪等性;可謂:商家美滋滋,買家罵咧咧!

防接口重復提交,這是必須要做的一件事情!

三、REST風格與冪等性

以常用的四種來分析哈!

REST

是否支持冪等

SQL例子

GET


SELECT * FROM table WHER id = 1

PUT


UPDATE table SET age=18 WHERE id = 1

DELETE


DELETE FROM table WHERE id = 1

POST


INSERT INTO table (id,age) VALUES(1,21)

所以我們要解決的就是POST請求!

四、解決思路

大概主流的解決方案:

  • token機制(前端帶著在請求頭上帶著標識,后端驗證)
  • 加鎖機制
  • 數據庫悲觀鎖(鎖表)
  • 數據庫樂觀鎖(version號進行控制)
  • 業務層分布式鎖(加分布式鎖redisson)
  • 全局唯一索引機制
  • redis的set機制
  • 前端按鈕加限制

小編的解決方案就是redis的set機制!

同一個用戶,任何POST保存相關的接口,1s內只能提交一次。

完全使用后端來進行控制,前端可以加限制,不過體驗不好!

后端通過自定義注解,在需要防冪等接口上添加注解,利用AOP切片,減少和業務的耦合!在切片中獲取用戶的token、user_id、url構成redis的唯一key!第一次請求會先判斷key是否存在,如果不存在,則往redis添加一個主鍵key,設置過期時間;

如果有異常會主動刪除key,萬一沒有刪除失敗,等待1s,redis也會自動刪除,時間誤差是可以接受的!第二個請求過來,先判斷key是否存在,如果存在,則是重復提交,返回保存信息!

五、實戰

SpringBoot版本為2.7.4。

1、導入依賴

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.16</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

2、編寫yml

server:
port: 8087

spring:
redis:
host: localhost
port: 6379
password: 123456
datasource:
#使用阿里的Druid
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC
username: root
password:

3、redis序列化

/**
* @author wangzhenjun
* @date 2022/11/17 15:20
*/
@Configuration
public class RedisConfig {

@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);

// 使用StringRedisSerializer來序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);

// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);

template.afterPropertiesSet();
return template;
}
}

4、自定義注解

/**
* 自定義注解防止表單重復提交
* @author wangzhenjun
* @date 2022/11/17 15:18
*/
@Target(ElementType.METHOD) // 注解只能用于方法
@Retention(RetentionPolicy.RUNTIME) // 修飾注解的生命周期
@Documented
public @interface RepeatSubmit {

/**
* 防重復操作過期時間,默認1s
*/
long expireTime() default;
}

5、編寫切片

異常信息大家換成自己想拋的異常,小編這里就沒有詳細劃分異常,就是為了寫博客而記錄的不完美項目哈!

/**
* @author wangzhenjun
* @date 2022/11/16 8:54
*/
@Slf4j
@Component
@Aspect
public class RepeatSubmitAspect {

@Autowired
private RedisTemplate redisTemplate;
/**
* 定義切點
*/
@Pointcut("@annotation(com.example.demo.annotation.RepeatSubmit)")
public void repeatSubmit() {}

@Around("repeatSubmit()")
public Object around(ProceedingJoinPoint joinPoint) throws{

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
// 獲取防重復提交注解
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
// 獲取token當做key,小編這里是新后端項目獲取不到哈,先寫死
// String token = request.getHeader("Authorization");
String tokenKey = "hhhhhhh,nihao";
if (StringUtils.isBlank(token)) {
throw new RuntimeException("token不存在,請登錄!");
}
String url = request.getRequestURI();
/**
* 通過前綴 + url + token 來生成redis上的 key
* 可以在加上用戶id,小編這里沒辦法獲取,大家可以在項目中加上
*/
String redisKey = "repeat_submit_key:"
.concat(url)
.concat(tokenKey);
log.info("==========redisKey ====== {}",redisKey);

if (!redisTemplate.hasKey(redisKey)) {
redisTemplate.opsForValue().set(redisKey, redisKey, annotation.expireTime(), TimeUnit.SECONDS);
try {
//正常執行方法并返回
return joinPoint.proceed();
} catch (Throwable throwable) {
redisTemplate.delete(redisKey);
throw new Throwable(throwable);
}
} else {
// 拋出異常
throw new Throwable("請勿重復提交");
}
}
}

6、統一返回值

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
private Integer code;
private String msg;
private T data;
//成功碼
public static final Integer SUCCESS_CODE = 200;
//成功消息
public static final String SUCCESS_MSG = "SUCCESS";
//失敗
public static final Integer ERROR_CODE = 201;
public static final String ERROR_MSG = "系統異常,請聯系管理員";
//沒有權限的響應碼
public static final Integer NO_AUTH_COOD = 999;
//執行成功
public static <T> Result<T> success(T data){
return new Result<>(SUCCESS_CODE,SUCCESS_MSG,data);
}
//執行失敗
public static <T> Result failed(String msg){
msg = StringUtils.isEmpty(msg)? ERROR_MSG : msg;
return new Result(ERROR_CODE,msg,"");
}
//傳入錯誤碼的方法
public static <T> Result failed(int code,String msg){
msg = StringUtils.isEmpty(msg)? ERROR_MSG : msg;
return new Result(code,msg,"");
}
//傳入錯誤碼的數據
public static <T> Result failed(int code,String msg,T data){
msg = StringUtils.isEmpty(msg)? ERROR_MSG : msg;
return new Result(code,msg,data);
}
}

7、簡單的全局異常處理

這是殘缺版,大家不要模仿!

/**
* @author wangzhenjun
* @date 2022/11/17 15:33
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(value = Throwable.class)
public Result handleException(Throwable throwable){
log.error("錯誤",throwable);
return Result.failed(500, throwable.getCause().getMessage());
}
}

8、controller測試

/**
* @author wangzhenjun
* @date 2022/10/26 16:51
*/
@RestController
@RequestMapping("/test")
public class TestController {

@Autowired
private SysLogService sysLogService;

// 默認1s,方便測試查看,寫10s
@RepeatSubmit(expireTime = 10)
@PostMapping("/saveSysLog")
public Result saveSysLog(@RequestBody SysLog sysLog){
return Result.success(sysLogService.saveSyslog(sysLog));
}
}

9、service

/**
* @author wangzhenjun
* @date 2022/11/10 16:45
*/
@Service
public class SysLogServiceImpl implements SysLogService {
@Autowired
private SysLogMapper sysLogMapper;
@Override
public int saveSyslog(SysLog sysLog) {
return sysLogMapper.insert(sysLog);
}
}

六、測試

1、postman進行測試

輸入請求:
http://localhost:8087/test/saveSysLog請求參數:

{
"title":"你好",
"method":"post",
"operName":"我是測試冪等性的"
}

發送請求兩次:

圖片

2、查看數據庫

只會有一條保存成功!

圖片

3、查看redisKey

在10s會自動刪除,就可以在次提交!

圖片

4、控制臺

圖片

七、總結

這樣就解決了冪等性問題,再也不會有錯誤數據了,減少了一個bug提交!這是一個都要重視的問題,必須要解決,不然可能會出現問題。

責任編輯:姜華 來源: 小王博客基地
相關推薦

2023-03-03 09:11:12

高并發SpringBoot

2025-10-24 07:52:56

2024-07-02 11:42:53

SpringRedis自定義

2024-10-09 10:46:41

springboot緩存redis

2024-04-03 09:18:03

Redis數據結構接口防刷

2025-02-23 08:00:00

冪等性Java開發

2024-05-28 09:26:46

2023-03-07 08:19:16

接口冪等性SpringBoot

2023-10-09 07:37:01

2024-06-14 09:30:58

2023-10-11 07:57:23

springboot微服務

2020-09-04 13:30:43

Java自定義代碼

2024-03-13 15:18:00

接口冪等性高并發

2023-10-24 13:48:50

自定義注解舉值驗證

2023-08-01 08:54:02

接口冪等網絡

2021-01-18 14:34:59

冪等性接口客戶端

2020-11-12 07:43:06

Redis冪等性接口

2024-04-01 08:11:20

2020-11-25 11:20:44

Spring注解Java

2022-11-01 11:15:56

接口策略模式
點贊
收藏

51CTO技術棧公眾號

国内伊人久久久久久网站视频 | 欧美亚洲日本一区| 日韩欧美亚洲日产国产| 国产裸体无遮挡| 国产日韩一区二区三区在线播放 | 51精品国产人成在线观看 | 高清免费电影在线观看| 成人免费视频一区二区| 国产精品成人免费电影| 久久精品99国产精| 精品免费一区二区| 精品日韩一区二区三区免费视频| 日韩中文字幕二区| 性欧美高清come| 国产三级一区二区| 国产91免费视频| 一级特黄aa大片| 午夜在线播放视频欧美| 欧美激情综合亚洲一二区| 一级黄色片网址| 日韩免费高清视频网站| 欧美日韩亚洲综合| 欧美牲交a欧美牲交| av在线app| 国产欧美精品日韩区二区麻豆天美| 91日韩久久| 亚洲天堂国产精品| 母乳一区在线观看| 国内精品久久久久伊人av | 日韩毛片在线一区二区毛片| 国产乱妇无码大片在线观看| 国产精品久久久久久久久久久久| 91精品国产乱码久久久张津瑜| 91精品福利| 中文字幕精品网| 受虐m奴xxx在线观看| 99国产精品久久一区二区三区| 欧美日本国产一区| 成人免费在线观看视频网站| 全亚洲第一av番号网站| 五月综合激情婷婷六月色窝| 97在线免费视频观看| 久久77777| 中文字幕一区在线观看视频| 日本亚洲欧洲精品| 欧美69xxxxx| 久久久久久夜精品精品免费| 久久伦理网站| 亚洲色图欧美视频| 91免费精品国自产拍在线不卡| 国产精品区一区| 丰满熟妇乱又伦| 成人免费视频一区| 国内精品久久国产| 蜜桃av鲁一鲁一鲁一鲁俄罗斯的| 国产精品一区一区| av成人免费观看| 成人福利小视频| 国产成人av电影免费在线观看| 99精品国产高清在线观看| 国产女人高潮时对白| 国产高清在线精品| 国产精品二区在线| 三级网站在线看| 91麻豆视频网站| 日韩av电影免费播放| 啊v在线视频| 1000精品久久久久久久久| 蜜桃视频成人在线观看| 动漫一区二区| 日韩欧美成人网| 亚洲成人福利在线观看| 伊人久久大香伊蕉在人线观看热v 伊人久久大香线蕉综合影院首页 伊人久久大香 | 色综合视频一区二区三区高清| 男人操女人免费软件| 亚洲天堂1区| 欧美一区二区三区在| 国产精品91av| 羞羞答答一区二区| 日韩亚洲第一页| 欧美成人aaa片一区国产精品| 国内精品久久久久久久97牛牛| 91精品国产精品| 欧美激情一区二区三区免费观看| 国内精品视频一区二区三区八戒| 成人免费看片网站| 黄色软件在线| 亚洲精品久久久久久国产精华液| 日韩欧美一区二| 麻豆久久久久| 亚洲精品99999| 成人午夜免费影院| 伊人成人在线| 国产日韩在线播放| 日韩一级免费视频| 国产精品欧美经典| 欧美黑人经典片免费观看| 97成人超碰| 亚洲成人免费网站| 亚洲综合图片一区| 亚洲少妇自拍| 91在线观看免费网站| 色视频免费在线观看| 亚洲欧洲精品天堂一级| 91黄色小网站| 日韩在线观看一区二区三区| 国产一区二区三区免费视频| 免费看一级一片| 奇米777欧美一区二区| 黑人中文字幕一区二区三区| 久操免费在线| 色av一区二区| 无码人妻aⅴ一区二区三区| 中文字幕亚洲精品乱码| 国产精品69av| 香蕉久久国产av一区二区| 亚洲私人黄色宅男| 性欧美极品xxxx欧美一区二区| 大奶在线精品| 欧美成人激情视频| 中文字幕一区2区3区| 91麻豆国产精品久久| 狠狠噜天天噜日日噜| 国语自产精品视频在线看抢先版结局| 日韩av在线免费观看一区| 五月婷婷一区二区| 国内精品伊人久久久久av影院| 视频一区视频二区视频| 成人一区福利| 亚洲精品久久久久久久久| 九九热国产在线| 国产一区二区三区不卡在线观看| 亚洲日本一区二区三区在线不卡 | 国产欧美一区二区三区四区| 免费人成黄页在线观看忧物| 福利精品视频在线| 娇妻高潮浓精白浆xxⅹ| 国产精品www994| 97在线电影| av网站免费在线观看| 在线播放欧美女士性生活| 美女三级黄色片| 久久精品99国产精品日本| 亚洲精品国产系列| 国产亚洲精彩久久| 在线观看精品自拍私拍| 中文字幕一区二区三区四区欧美| 2021久久国产精品不只是精品| 久久亚洲中文字幕无码| 日韩福利视频一区| 4p变态网欧美系列| 撸视在线观看免费视频| 日本高清不卡aⅴ免费网站| 亚洲综合色一区| 日韩极品在线观看| 亚洲人成77777| 亚洲精品66| 欧美成年人在线观看| 亚洲精品一区二区三区蜜桃| 亚洲影院在线观看| 日韩精品人妻中文字幕有码| 亚洲一区网站| 日韩欧美在线一区二区| 亚洲成人精品综合在线| 久久久精品在线观看| 亚洲第九十九页| 亚洲成av人片在线观看无码| 国产精品无码网站| 蜜桃精品视频在线| 久久99国产精品一区| 盗摄牛牛av影视一区二区| 久久久久免费精品国产| 日本一二三区在线视频| 欧美日韩免费不卡视频一区二区三区 | 亚洲乱码一区二区三区在线观看| 日本少妇激三级做爰在线| 欧美日韩亚洲一区二区三区在线| 国内视频一区| 成人福利一区二区| 久久91精品国产91久久跳| 亚洲欧美高清视频| 色综合视频在线观看| 国产黄a三级三级| 福利电影一区二区| 激情五月婷婷久久| 欧美黄色一区| 日韩三级电影免费观看| 伊人久久影院| 国产精品久久久av久久久| a级片国产精品自在拍在线播放| 亚洲级视频在线观看免费1级| 免费黄色一级大片| 亚洲综合一区二区| 欧美午夜激情影院| 国产91精品一区二区麻豆亚洲| 国产欧美高清在线| 午夜精品久久| 日韩性感在线| 欧美日韩一区二区三区四区不卡| 国产精品网址在线| 在线天堂资源www在线污| 久久夜色精品亚洲噜噜国产mv| 天天操天天干天天干| 欧美男女性生活在线直播观看| 黄色激情视频在线观看| 亚洲欧洲日韩女同| 色一情一交一乱一区二区三区| 国产成人欧美日韩在线电影| 青青草精品视频在线观看| 激情亚洲成人| 亚洲天堂av免费在线观看| 九九久久精品| 久久99影院| baoyu135国产精品免费| 国产日韩中文字幕在线| 波多野结衣久久精品| 欧美黑人性生活视频| 久久黄色美女电影| 在线日韩欧美视频| 欧美视频综合| 日韩av中文字幕在线播放| 亚洲精品成人电影| 91精选在线观看| 又污又黄的网站| 一本一道综合狠狠老| 日韩精品一区二区在线播放| 亚洲精选视频在线| 国产suv精品一区二区68| 欧美激情中文字幕| 久久美女免费视频| 久久一区二区三区四区| 中文文字幕文字幕高清| 成人自拍视频在线观看| 免费看三级黄色片| 国产精品资源网| 九九热精品国产| 久久99国产乱子伦精品免费| 在线免费观看视频黄| 日韩国产精品91| 久久午夜夜伦鲁鲁一区二区| 久久精品一区| 欧美激情精品久久久久久小说| 国产精品久久久久毛片大屁完整版| 日本人体一区二区| 亚洲伦理精品| 欧美 国产 综合| 亚洲欧美日韩国产| 99视频在线免费| 日本免费在线视频不卡一不卡二| 天天视频天天爽| 久久99最新地址| 国产精品一级无码| a级高清视频欧美日韩| 在线免费播放av| 91毛片在线观看| 日本成人午夜影院| 国产精品成人网| 久草网视频在线观看| 午夜av一区二区三区| 三级网站在线播放| 欧美男女性生活在线直播观看| av网站在线观看免费| 欧美tickling网站挠脚心| 亚洲aaa在线观看| 国产一区二区三区网站| 日韩av中文| 欧美黑人一区二区三区| 免费日韩电影| 91精品国产综合久久香蕉最新版 | 欧美黑人粗大| 国产日韩精品电影| 北条麻妃一区二区三区在线观看| 精品一区久久| 香蕉综合视频| www.射射射| 日韩精品每日更新| 亚洲欧美手机在线| 99久久国产综合精品色伊| 一级黄色录像毛片| 一区二区三区在线播| 精品国产午夜福利| 欧美人成免费网站| 国产 日韩 欧美 综合| 亚洲视频777| 国产原创精品视频| 国产91精品久久久久| 日韩成人一区| 久久国产精品亚洲va麻豆| 日韩电影一区| 欧美视频在线观看网站| 久久www免费人成看片高清| zjzjzjzjzj亚洲女人| 国产午夜亚洲精品午夜鲁丝片| 91视频免费在线看| 91久久久免费一区二区| 亚洲第一天堂在线观看| 有码中文亚洲精品| 成年网站在线视频网站| 国产精品揄拍一区二区| 久久国产精品免费精品3p| 日本特级黄色大片| 欧美综合二区| 亚洲中文字幕无码一区| 国产精品福利电影一区二区三区四区| 亚州国产精品视频| 欧美一区二区精品| aaa在线观看| 欧美一级淫片丝袜脚交| 久久伊人精品| 亚洲精品人成| 欧美专区18| 妖精视频一区二区| 亚洲人123区| 在线免费av片| 亚洲色图50p| 久草在线资源福利站| 99国产精品久久久久老师| 91精品啪在线观看国产18| 老司机午夜av| 久久综合九色综合97婷婷| 久久艹精品视频| 欧美一区二区三区四区高清| av电影在线观看网址| 日本伊人精品一区二区三区介绍 | 亚洲国产小视频在线观看| aaa大片在线观看| 91久久综合亚洲鲁鲁五月天| 欧美日韩在线播放视频| 北条麻妃视频在线| 久久尤物电影视频在线观看| 免费看日韩毛片| 亚洲的天堂在线中文字幕| 任你弄在线视频免费观看| 95av在线视频| 欧美激情性爽国产精品17p| 一级黄色片国产| 亚洲日本一区二区| 91麻豆成人精品国产免费网站| 一区二区成人精品| 国产精品无码久久久久| 亚洲欧洲一区二区福利| 日本sm残虐另类| 蜜桃av.com| 7777精品伊人久久久大香线蕉的| 日韩毛片久久久| 国产一区二区色| 国产精品国产三级国产在线观看| 午夜在线观看av| 亚洲天堂网中文字| 99国产在线播放| 欧美精品www| 精品亚洲自拍| 免费看的黄色大片| 久久久不卡影院| 中文精品久久久久人妻不卡| 少妇高潮 亚洲精品| 欧美97人人模人人爽人人喊视频| 伊人久久大香线蕉午夜av| 久久成人羞羞网站| 欧美日韩大片在线观看| 亚洲精品mp4| 吞精囗交69激情欧美| 色一情一乱一伦一区二区三区丨| 秋霞电影网一区二区| 国产精品视频一区二区三| 日韩情涩欧美日韩视频| f2c人成在线观看免费视频| 欧美第一黄网| 精品一区二区综合| 精品少妇久久久久久888优播| 亚洲精品久久视频| 国产韩日精品| 影音先锋成人资源网站| av不卡免费在线观看| 久久久久久亚洲av无码专区| 日韩亚洲第一页| 老牛精品亚洲成av人片| 校园春色 亚洲色图| 一区二区三区在线播放| 青青免费在线视频| 91美女福利视频高清| 亚洲精品少妇| 中文字幕第69页| 亚洲精品电影在线| 黄色精品视频网站| 欧美精品久久久久久久久久久| 中文字幕精品一区二区三区精品| 99视频国产精品免费观看a| 欧美亚洲国产日本| 色97色成人| 国产女人18毛片水真多18| 欧美三级视频在线观看| 高清电影在线免费观看| 亚洲精品久久久久久一区二区| 岛国av在线一区| 91在线视频国产| 欧美有码在线视频| 欧美成人69| 四虎影视一区二区| 亚洲美女在线观看|