精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

五月天久久比比资源色| 精品一区二区av| 亚洲人成电影在线| 超碰在线公开97| 成人看av片| 成人h精品动漫一区二区三区| 91干在线观看| 尤物在线免费视频| 亚洲成人一品| 欧美一级免费大片| 欧美黄色一级片视频| a黄色片在线观看| 久久亚洲精华国产精华液| 国产欧美精品va在线观看| 麻豆changesxxx国产| 欧美极品在线观看| 日韩欧美久久一区| 国产视频在线视频| 888av在线视频| 成人免费小视频| 欧美久久久久久久| 亚洲经典一区二区三区| 日本欧美久久久久免费播放网| 色中色综合影院手机版在线观看 | 日本高清www免费视频| 久久影视一区| 亚洲最大中文字幕| 亚洲观看黄色网| 一本一道久久a久久| 欧美日韩高清一区二区不卡 | 三级影片在线看| 成人vr资源| 国产亚洲精品美女久久久久| 深田咏美中文字幕| 日韩视频在线直播| 欧美精品一级二级| 天天操天天爱天天爽| 午夜伦理福利在线| 精品人伦一区二区三区蜜桃网站 | 日韩影片在线播放| 男女网站在线观看| 99这里只有久久精品视频| 51国偷自产一区二区三区的来源| 亚洲图片欧美日韩| 中日韩视频在线观看| 国模精品系列视频| 四虎成人精品永久免费av| 欧美在线影院| 欧美黑人xxx| 久一视频在线观看| 国产尤物精品| 久久久久久久久久久久久久久久久久av | 久久日一线二线三线suv| 高清av免费一区中文字幕| 精品国产av 无码一区二区三区 | 91久久在线| 国产最新精品视频| 久久久午夜影院| 久久久久91| 国产精品日韩欧美综合| 中文字幕 日韩有码| 免费高清视频精品| 成人久久精品视频| 成人h动漫精品一区二区无码 | 国产精品综合激情| 久久综合av| 久久成人在线视频| 免费在线一区二区三区| 亚洲大胆av| 日本a级片电影一区二区| 高清乱码免费看污| 日本va欧美va瓶| 91网站在线免费观看| 国产高中女学生第一次| 97久久精品人人做人人爽50路| 久久偷窥视频| eeuss影院www在线播放| 亚洲日本护士毛茸茸| 免费看毛片的网址| 欧美xxx性| 欧美一区二区在线看| 91精产国品一二三| 亚洲激情播播| www国产精品视频| 久久精品波多野结衣| 一区二区日韩免费看| 国产精品∨欧美精品v日韩精品| 依依成人在线视频| 国产成人在线电影| 欧美一区免费视频| a视频在线观看免费| 午夜av电影一区| 超碰在线播放91| 91午夜精品| 伊人久久综合97精品| 免费日韩在线视频| 日韩电影免费在线观看网站| 91在线无精精品一区二区| 少妇高潮一区二区三区69| 国产视频一区在线播放| 91国在线高清视频| 久久爱91午夜羞羞| 日韩欧美在线网站| 一本加勒比北条麻妃| 亚洲成人99| 日本最新高清不卡中文字幕| 国产美女裸体无遮挡免费视频| 91亚洲国产成人精品一区二三| 在线观看欧美亚洲| 刘亦菲一区二区三区免费看| 日韩一卡二卡三卡国产欧美| 久久久久久久久久久久久久久| 一区二区三区中文| 国产91色在线播放| 黄色片一区二区三区| 中文字幕中文字幕在线一区| 国产成人a亚洲精v品无码| 狂野欧美xxxx韩国少妇| 一本色道久久综合狠狠躁篇怎么玩| 久久久久久久久久综合| 久久精品国产色蜜蜜麻豆| 久久精品国产第一区二区三区最新章节 | 色999韩欧美国产综合俺来也| 亚洲福利视频网| 色欲人妻综合网| 蜜桃免费网站一区二区三区| 免费99视频| www欧美xxxx| 欧美一区在线视频| 少妇的滋味中文字幕bd| 每日更新成人在线视频| 国产精品国色综合久久| 黄色在线免费看| 精品视频在线视频| 亚洲国产日韩一区无码精品久久久| 最新成人av网站| 国产精品免费观看高清| 超碰在线观看免费| 8x福利精品第一导航| 免费黄在线观看| 视频一区二区国产| 欧美一区2区三区4区公司二百| 欧美三级网站| 亚洲成色999久久网站| 欧美黑人猛猛猛| 国产麻豆成人精品| 最新精品视频| 99er精品视频| 伦理中文字幕亚洲| www.黄色片| 夜夜精品视频一区二区| 免费看三级黄色片| 狠狠久久婷婷| 极品尤物一区二区三区| 高清视频在线观看三级| 亚洲激情中文字幕| 亚洲另类欧美日韩| 久久青草国产手机看片福利盒子 | 欧美成人国产精品高潮| 国产在线观看免费一区| 日韩中文字幕亚洲精品欧美| 欧洲大片精品免费永久看nba| 精品自在线视频| 亚洲第一天堂网| 天天爽夜夜爽夜夜爽精品视频 | 国产精品国产馆在线真实露脸| 欧美在线aaa| 在线观看国产精品入口| 91精品久久久久久蜜桃| www.综合网.com| 精品调教chinesegay| 91在线视频免费播放| 国产精品视频第一区| 亚洲欧美一区二区三区不卡| 欧美国产高清| 久久riav二区三区| 无人区在线高清完整免费版 一区二| 中文字幕在线精品| 99精品视频免费看| 亚洲 欧美综合在线网络| 日本xxx在线播放| 日本女人一区二区三区| 看一级黄色录像| 蜜桃久久久久| 国产精品天天狠天天看| 成人免费高清| 亚洲精品自拍偷拍| 91国内精品久久久| 亚洲一区二区三区中文字幕在线 | 成人久久久精品国产乱码一区二区| 精品高清美女精品国产区| 日本少妇xxxxx| 国产成人一区二区精品非洲| 日韩网址在线观看| 小小影院久久| 六十路精品视频| 99视频有精品高清视频| 97香蕉超级碰碰久久免费软件| 成黄免费在线| 亚洲国产欧美一区二区三区同亚洲| 久久久久久久久久一级| 一区二区高清在线| 国产视频不卡在线| av一区二区三区黑人| 一路向西2在线观看| 亚洲国产综合在线看不卡| 亚洲精品视频一区二区三区| 超碰成人福利| 成人在线一区二区| www.日韩| 欧美国产中文字幕| 精品国产丝袜高跟鞋| 亚洲色图美腿丝袜| 人妻一区二区三区四区| 欧美日韩精品欧美日韩精品| 九九热在线免费观看| 亚洲免费观看高清完整版在线观看熊| 国产成人无码精品久久二区三| 国产精品白丝jk白祙喷水网站| 成人亚洲视频在线观看| 在线免费高清一区二区三区| 可以在线看黄的网站| 欧美三级伦理在线| 久久久久久久久久久久久久一区| 精品入口麻豆88视频| 国产精品视频自拍| 免费在线观看一区| 国产成人午夜视频网址| 国产精品vvv| 欧美激情视频网| √天堂8在线网| 久久久成人精品| 午夜在线视频| 一区二区三区美女xx视频| 青青草娱乐在线| 日韩精品中文字| 香港一级纯黄大片| 亚洲高清av在线| 色呦呦免费观看| 精品福利二区三区| 丰满大乳国产精品| 亚洲精品一区二区三区精华液 | 亚洲日本成人网| 欧洲一区av| 国产香蕉精品视频一区二区三区| 手机福利在线| 亚洲免费av网址| 日本一本草久在线中文| 精品视频在线导航| 精品无吗乱吗av国产爱色| 亚洲欧美日本精品| 你懂的在线免费观看| 国产婷婷97碰碰久久人人蜜臀| 天堂av在线资源| 亚洲欧美日韩一区在线| 黄网在线观看| 色哟哟网站入口亚洲精品| 91短视频版在线观看www免费| 最近2019年中文视频免费在线观看 | 日韩在线黄色| 欧美二区在线| 日韩欧美午夜| 日本在线视频www色| 永久亚洲成a人片777777| 91动漫在线看| 久久综合图片| 亚洲第一天堂久久| 国产成人精品1024| 人妻体内射精一区二区| 欧美高清在线精品一区| 永久免费看片直接| 亚洲午夜久久久久久久久电影网| 日韩 欧美 亚洲| 在线观看视频欧美| 国产一区二区在线不卡| 日韩欧美黄色影院| 香蕉国产在线视频| 综合国产在线视频| 久久大胆人体| 国产精品精品视频| 欧美高清hd| 蜜桃麻豆91| 亚洲精品国产偷自在线观看| 99在线观看视频免费| 久久午夜影视| 免费成人黄色大片| 99v久久综合狠狠综合久久| 性猛交娇小69hd| 亚洲精品乱码久久久久久| 日韩色图在线观看| 91麻豆精品国产91久久久久久 | 久久精品91久久久久久再现| 欧美大胆的人体xxxx| 欧美中文字幕在线| 精品国产亚洲一区二区三区在线| 国产尤物99| 一本到12不卡视频在线dvd| 18岁网站在线观看| 国模大尺度一区二区三区| 欧美肉大捧一进一出免费视频| 国产精品美女久久久久aⅴ| 国产真实乱人偷精品视频| 欧美丝袜丝nylons| 姝姝窝人体www聚色窝| 精品国产一区久久久| 625成人欧美午夜电影| 亚洲一区二区三区在线免费观看 | 九九久久九九久久| 奇米精品一区二区三区四区| 极品白嫩的小少妇| 国产精品成人一区二区三区夜夜夜| 永久免费看片在线播放| 欧美一区二区三区婷婷月色| 国产资源在线看| 91精品91久久久久久| 精品国产乱码久久久久久樱花| 日韩国产精品一区二区三区| 悠悠资源网久久精品| 中文字幕avav| 国产精品久久久久久久第一福利 | xxxx日本少妇| 欧美日韩综合不卡| 国产天堂素人系列在线视频| 98精品国产自产在线观看| 一区二区三区视频播放| 日本三级中文字幕在线观看| 精品在线你懂的| 国产又黄又粗的视频| 色呦呦一区二区三区| 无码精品在线观看| 久久久久免费视频| 一区二区三区四区精品视频| av不卡在线免费观看| 美女国产一区二区三区| 少妇久久久久久久久久| 欧美日韩亚洲一区二区三区| 手机av在线免费观看| 国模私拍视频一区| 男人的天堂久久| 成人免费毛片网| 91首页免费视频| 国产情侣自拍av| 日韩国产高清视频在线| av白虎一区| 久久手机视频| 天堂资源在线中文精品| 亚洲码无人客一区二区三区| 日韩欧美在线观看| 国产福利电影在线| 国产成人一区二区三区电影| 国产亚洲一区二区三区啪| 韩国中文字幕av| 国产精品国产三级国产有无不卡 | 三级黄色在线视频| 日韩精品视频免费专区在线播放 | 岛国成人毛片| 成人欧美一区二区三区视频| 综合亚洲视频| 国产人妻黑人一区二区三区| 亚洲.国产.中文慕字在线| 日本护士...精品国| 国产精品99久久99久久久二8| 欧美电影一二区| 中文字幕永久免费| 午夜国产精品影院在线观看| 日本一级在线观看| 国产在线观看91精品一区| 7777久久香蕉成人影院| 亚洲国产精品第一页| 欧美午夜性色大片在线观看| 成年人视频网站在线| 91精品黄色| 午夜在线a亚洲v天堂网2018| 青娱乐国产视频| 欧美一区日本一区韩国一区| av影片在线| 午夜精品亚洲一区二区三区嫩草 | 成年网站在线视频网站| 久久久久久欧美精品色一二三四| 日韩一区精品字幕| 超碰在线国产97| 精品国产一区a| 免费在线观看一区| 久久久国内精品| 久久久美女毛片| 999久久久久久| 日韩美女在线看| 亚洲精品va| 欧美熟妇激情一区二区三区| 日韩欧美电影在线| 外国成人直播| 97在线国产视频| 国产精品久久久久久久久免费樱桃| 精品人妻少妇AV无码专区| 日韩美女激情视频| 国色天香一区二区| youjizz亚洲女人| 日韩成人中文字幕| 成人永久在线| 能看的毛片网站|