成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

搞了個線上故障,被老板罵了....

開發 架構
本文的案例有點特殊,訂單號是后端系統生成的,前后兩次請求無法區分重復狀態,所以系統會創建兩條不同訂單 ID 記錄,繞過了「唯一索引約束」這個限制。

大家好,我是Tom哥。

前幾天跟一位小伙伴聊天,心情特別沮喪,剛被老板罵完.....

差點丟了飯碗,還好老板沒說 “滾”。

就今年這就業行情,滿眼都是淚哇。

小伙伴在一家初創公司,團隊規模很小,老板為了節省成本,也沒配置什么豪華陣容。

他的工作時間也不長,負責交易訂單,前幾天接到用戶投訴,「我的訂單列表」有多條一模一樣的訂單。

雖沒造成什么資損,但嚴重影響用戶體驗。

看到這里,有經驗的同學可能猜到,應該是接口沒做防重控制。

日常開發中,重復提交也是蠻常見問題。

比如:用戶提交一個表單,鼠標點的太快,正好前端又是個新兵蛋子,沒做任何控制,瞬間就會有多個請求發到后端系統。

如果后端同學也沒做兜底方案的話,悲劇就發生了。

常見的解決方案是借助數據庫自身的「唯一索引約束」,來保證數據的準確性,這種方案一般在插入場景用的多些。

變種方案可以考慮單獨創建一個防重表。

本文的案例有點特殊,訂單號是后端系統生成的,前后兩次請求無法區分重復狀態,所以系統會創建兩條不同訂單 ID 記錄,繞過了「唯一索引約束」這個限制,這.....

另外,MySQL 性能也單薄了點,單機 QPS 在「千」維度,如果是面對一個高并發接口,性能也有點吃緊。

接下來,我們就來講下,借助 Redis 來實現接口防重復提交。

技術方案

首先,我們來看下整理的流程,如下圖所示:

圖片

大致步驟:

1、客戶端發送請求到服務端。

2、服務端接收請求,然后從請求參數中提取唯一標識。這個標識可以沒有什么特殊業務含義,client 端隨機生成即可。

3、服務端系統將唯一標識先嘗試寫入 Redis 緩存中,可以認為是加鎖操作。

4、加鎖失敗,說明請求還在處理,此次是重復請求,可以丟棄。

5、加鎖成功,繼續后面正常業務邏輯處理。

6、業務邏輯處理完成后,刪除加鎖的標記。

7、最后,將處理成功的結果返回給客戶端。

注意事項:

  • 重復提交場景一般都是在極短時間內,同時發送了多次請求(比如:頁面表單重復提交),我們只認第一次請求為有效請求。
  • 鎖用完后,要記得手動刪除。為了防止鎖沒有正常釋放,我們可以為鎖設置一個極短的過期時間(比如 10 秒)。

項目實戰

1、引入 redis 組件

實戰的項目采用 Spring Boot 搭建,這里需要引入 Redis 相關依賴。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

2、redis 變量配置

application.properties 配置文件中,添加redis相關服務配置。

spring.redis.host=127.0.0.1
spring.redis.port=6379

3、定義注解類

定義一個注解,配置在需要防重復的接口方法上,提高開發效率,同時降低代碼的耦合度。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented

public @interface IdempotentRule {

/**
* 業務自定義前綴
*/
String prefix() default "";

/**
* 業務重復標識
*/
String key() default "";
}

4、接口攔截器

上面定義了IdempotentRule?注解,需要通過攔截器對正常的業務方法做攔截,增加一些特殊邏輯處理。

@Aspect
@Component
@Slf4j
public class IdempotentAspect {

@Autowired
private RedisTemplate<String, Serializable> idempotentRedisTemplate;

@Around("execution(public * *(..)) && @annotation(com.onyone.idempotent.annotation.IdempotentRule)")
public Object limit(ProceedingJoinPoint pjp) {
MethodSignature signature = (MethodSignature) pjp.getSignature();


Object[] params = pjp.getArgs();
String[] paramNames = signature.getParameterNames();

Method method = signature.getMethod();
IdempotentRule idempotentRule = method.getAnnotation(IdempotentRule.class);
String key = idempotentRule.key();
String prefix = idempotentRule.prefix();

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext();
context.setVariable(paramNames[0], params[0]);
String repeatKey = (String) parser.parseExpression(key).getValue(context);

try {
// 先在緩存中做個標記
Boolean lockResult = idempotentRedisTemplate.opsForValue().setIfAbsent(prefix + repeatKey, "正在處理....", 20, TimeUnit.SECONDS);
if (lockResult) {
// 業務邏輯處理
return pjp.proceed();
} else {
throw new Exception("重復提交..................");
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
// 處理完成后,將標記刪除
idempotentRedisTemplate.delete(prefix + repeatKey);
}

return null;
}


}

這里,比較特殊的是提取請求的唯一標識,由于不同的業務請求唯一標識不一樣。

所以,這里采用 SPEL 表達式,將規則設置能力開放出去,由業務方自己定義,比如:

@IdempotentRule(key = "#userParam.cardNumber", prefix = "repeat_")。

攔截器根據 SPEL 表達式( 如 "#userParam.cardNumber")以及請求參數對象,計算當前請求唯一標識的值,

然后將值寫入 Redis 中,并設置過時間。

如果設置成功,說明是第一次請求,繼續下面的業務邏輯處理;否則,判定為重復請求,直接丟棄。

5、上層業務接口

@RestController
@RequestMapping("/user")
public class UserController {
/**
* 創建一個新的用戶
*/
@RequestMapping(value = "/create_user")
@IdempotentRule(key = "#userParam.cardNumber", prefix = "repeat_")
public String createUser(@RequestBody UserParam userParam) {
// 模擬業務處理

return "創建用戶成功!";
}
}
@Data
public class UserParam {
private String cardNumber;
private String name;
}

測試結果

1、構造客戶端請求,第一次處理成功。

圖片

2、 Redis 緩存中,能查到請求設置的鎖標記。

圖片

3、模擬重復,連續多次快速提交請求,請求會被攔截,并拋出異常。

圖片

責任編輯:姜華 來源: 微觀技術
相關推薦

2022-03-23 18:00:34

循環CPU線程

2022-07-28 14:22:50

元宇宙AI

2023-04-24 08:12:59

2024-09-10 14:50:00

2024-08-12 00:00:00

NPMCTOJavaScrip

2020-07-09 09:08:24

Java系統故障

2020-07-13 07:49:38

Kubernetes容器架構

2009-10-28 09:14:43

員工分析軟件

2022-04-08 08:48:16

線上事故日志訂閱者

2021-09-27 10:15:10

故障業務方電腦

2024-03-11 08:51:08

JVMSWAP內存

2021-08-05 06:46:39

P0故障公司

2020-04-13 17:17:28

MySQL8.0功能

2019-06-27 22:23:56

谷歌Android開發者

2021-05-12 09:15:48

Facebook 開發技術

2021-04-13 17:17:08

線上故障交付

2020-07-15 08:10:00

瀏覽器安全Fetch

2022-02-07 15:12:17

系統日志定位

2019-03-29 10:22:08

Linux系統故障技巧

2020-05-18 07:50:47

線上故障排查
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品国产欧美一区二区 | 精品国产伦一区二区三区观看体验 | 91免费在线播放 | 福利视频一区 | 91精品久久久久久久 | 99re视频这里只有精品 | 亚洲精品粉嫩美女一区 | a免费观看 | 久久国内 | 国产精品久久久久久网站 | 一区天堂 | 91成人精品视频 | 亚洲国产一区二区视频 | 看亚洲a级一级毛片 | 欧美激情欧美激情在线五月 | 91精品在线播放 | 天天干视频 | 北条麻妃一区二区三区在线观看 | 国产精品美女一区二区三区 | 中文字幕 国产 | 久久国产精品视频 | 91精品久久| 91视频进入 | 老司机深夜福利网站 | 99精品国产一区二区三区 | 人人做人人澡人人爽欧美 | 成人性生交大免费 | 日韩欧美精品在线 | 久久精品免费 | 国产精品a一区二区三区网址 | 国产探花在线精品一区二区 | www.99re| 亚洲 中文 欧美 日韩 在线观看 | 国产免费一区二区 | 欧美成视频在线观看 | 99国内精品久久久久久久 | 国产精品久久久久无码av | 一片毛片| 中文字幕在线观看国产 | 91欧美激情一区二区三区成人 | 精品国产亚洲一区二区三区大结局 |