網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
文章目錄
- 簡(jiǎn)介
- 冪等性如何實(shí)現(xiàn)
- 前端應(yīng)當(dāng)處理
- 后端基于 token + redis 處理
簡(jiǎn)介
-
接口的冪等性是指:
用戶對(duì)同一個(gè)操作發(fā)起多次請(qǐng)求,系統(tǒng)的設(shè)計(jì)需要保證其多次請(qǐng)求后結(jié)果是一致的。常見(jiàn)的如支付場(chǎng)景,連續(xù)快速點(diǎn)擊兩次支付 10 元,不應(yīng)該扣費(fèi) 20,第一次應(yīng)該扣費(fèi)成功,第二次則應(yīng)該失敗 -
什么場(chǎng)景要考慮冪等性設(shè)計(jì)
- 在現(xiàn)在公司里頭,都在做分布式劃分,一個(gè)大型系統(tǒng)會(huì)拆成比較多的模塊,不同的模塊用集群來(lái)部署,上游服務(wù)調(diào)用下游服務(wù)的形式,比如 rpc 調(diào)用,但是由于網(wǎng)絡(luò)抖動(dòng)等原因,可能會(huì)造成服務(wù)遲遲沒(méi)有響應(yīng),用戶端可能多次點(diǎn)擊
- 再一個(gè)就是支付場(chǎng)景也必須得考慮一下冪等性的問(wèn)題
- select 是天然冪等的操作,需要注意的是編輯相關(guān)的操作
冪等性如何實(shí)現(xiàn)
前端應(yīng)當(dāng)處理
前端一般對(duì)比如支付按鈕需要加上頻控,避免過(guò)快的連續(xù)點(diǎn)擊
后端基于 token + redis 處理
想法:
兩次針對(duì)同一訂單的付款請(qǐng)求,如果每次這樣的請(qǐng)求有一個(gè)唯一憑證,每次這個(gè)憑證訪問(wèn)到后端,看看 redis 有沒(méi)有,有的話請(qǐng)求執(zhí)行,redis 中刪除憑證,沒(méi)有的話請(qǐng)求失敗。這樣同樣的 2 個(gè)請(qǐng)求就可以保證第一次成功第二次失敗
具體步驟:
- 步驟一:客戶端要付款前,即在點(diǎn)擊付款按鈕前,客戶端先發(fā)送一個(gè)請(qǐng)求獲取 token,服務(wù)端生成一個(gè)全局唯一 id 作為 token,保存到 redis 中,同時(shí)把這個(gè) id 返回給客戶端
// 訂單生成后,然后立馬一個(gè)獲取 token 的請(qǐng)求過(guò)來(lái)
// 從請(qǐng)求中獲取用戶 userId
userId := ...
// 雪花算法,或者大廠自己封裝出來(lái)的依賴包的使用
token := ...
// redis 存儲(chǔ)此全局唯一 id 并且設(shè)置過(guò)期時(shí)間 30 min
redisClient.Set(ctx, "order:token"+userId, token, time.Minute*30)
// 業(yè)務(wù)操作,返回 token 全局唯一數(shù)據(jù)
- 步驟二:客戶端點(diǎn)擊付款,會(huì)帶上這個(gè) token id
- 步驟三:服務(wù)端接收到 token id,去查 redis 能夠查詢到,于是放行后續(xù)業(yè)務(wù)操作,同時(shí)刪除 redis 的 id
// 先通過(guò)請(qǐng)求參數(shù)拿到全局唯一 id
token := ...
// redis 通過(guò) lua 執(zhí)行原子操作判定此次請(qǐng)求是否執(zhí)行還是失敗返回,lua 原子操作的原因是防止并發(fā)時(shí)都執(zhí)行了
scriptStr := "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"
script := redis.NewScript(scriptStr)
cmd := script.Run(ctx, redisClient, []string{"order:token"+userId}, token)
// 處理 err,失敗返回客戶端說(shuō)明失敗
if cmd.Err() != nil {
// 返回扣款失敗
}
// redis 原子操作查找刪除是否成功
if v, _ := cmd.(int); v == 0 {
// 不成功返回客戶端說(shuō)明扣款失敗
}
// 執(zhí)行后續(xù)業(yè)務(wù)邏輯操作
- 步驟四:客戶端緊接著快速點(diǎn)了第二次付款,但是這時(shí)候請(qǐng)求打到服務(wù)端發(fā)現(xiàn) redis 已經(jīng)沒(méi)了此 token,因此第二次付款失敗
注意:
- 全局唯一 id 可以通過(guò)雪花算法,很多大公司也有封裝的依賴包拿到全局 id,需要全局唯一 id 的原因也因?yàn)槭欠植际降沫h(huán)境,需要保證在集群中保證 id 唯一
- 第一次和第二次付款請(qǐng)求需要保證 redis 多個(gè)操作的原子性,比如先查詢 redis,能查到再刪除 redis 這兩步操作合在一起不具備原子性,因此需要用天然支持原子性的 lua 腳本去執(zhí)行,這樣能保證并發(fā)快速點(diǎn)擊多次付款后不會(huì)出現(xiàn)這樣的并發(fā)場(chǎng)景:一個(gè)執(zhí)行到 if(是否能查到),另一個(gè)夜之星 if(是否能查到),這樣就會(huì)導(dǎo)致都判定走后續(xù)的付款邏輯
原文鏈接:https://abcnull.blog.csdn.net/article/details/129252022
- 上一篇:沒(méi)有了
- 下一篇:沒(méi)有了
相關(guān)推薦
- 2023-10-15 AddressSanitizer 查找內(nèi)存問(wèn)題
- 2022-10-19 C#實(shí)現(xiàn)自動(dòng)生成電子印章_C#教程
- 2022-05-28 Redis如何使用樂(lè)觀鎖(CAS)保證數(shù)據(jù)一致性_Redis
- 2022-10-06 Python交互Redis的實(shí)現(xiàn)_Redis
- 2022-08-19 存儲(chǔ)引擎的應(yīng)用場(chǎng)景
- 2022-02-17 springBoot自定義場(chǎng)景啟動(dòng)器starter
- 2022-10-04 一文教會(huì)你用Python讀取PDF文件_python
- 2022-05-08 ASP.NET?MVC異常過(guò)濾器用法_實(shí)用技巧
- 欄目分類
-
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支