自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

大家好,我是Tom哥。

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

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

就今年這就業(yè)行情,滿眼都是淚哇。

小伙伴在一家初創(chuàng)公司,團隊規(guī)模很小,老板為了節(jié)省成本,也沒配置什么豪華陣容。

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

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

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

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

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

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

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

變種方案可以考慮單獨創(chuàng)建一個防重表。

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

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

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

技術方案

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

圖片

大致步驟:

1、客戶端發(fā)送請求到服務端。

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

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

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

5、加鎖成功,繼續(xù)后面正常業(yè)務邏輯處理。

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

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

注意事項:

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

項目實戰(zhàn)

1、引入 redis 組件

實戰(zhàn)的項目采用 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、定義注解類

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

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

public @interface IdempotentRule {

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

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

4、接口攔截器

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

@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) {
// 業(yè)務邏輯處理
return pjp.proceed();
} else {
throw new Exception("重復提交..................");
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
// 處理完成后,將標記刪除
idempotentRedisTemplate.delete(prefix + repeatKey);
}

return null;
}


}

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

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

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

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

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

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

5、上層業(yè)務接口

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

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

測試結果

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

圖片

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

圖片

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

圖片

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

2022-03-23 18:00:34

循環(huán)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系統(tǒng)故障

2022-04-08 08:48:16

線上事故日志訂閱者

2009-10-28 09:14:43

員工分析軟件

2020-07-13 07:49:38

Kubernetes容器架構

2021-09-27 10:15:10

故障業(yè)務方電腦

2021-08-05 06:46:39

P0故障公司

2024-03-11 08:51:08

JVMSWAP內(nèi)存

2020-04-13 17:17:28

MySQL8.0功能

2019-06-27 22:23:56

谷歌Android開發(fā)者

2021-04-13 17:17:08

線上故障交付

2021-05-12 09:15:48

Facebook 開發(fā)技術

2024-07-01 08:22:00

2020-07-15 08:10:00

瀏覽器安全Fetch

2022-02-07 15:12:17

系統(tǒng)日志定位

2020-05-18 07:50:47

線上故障排查
點贊
收藏

51CTO技術棧公眾號