日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無(wú)先后,達(dá)者為師

網(wǎng)站首頁(yè) 編程語(yǔ)言 正文

接口冪等性的通用解決方案golang版

作者:abcnull 更新時(shí)間: 2023-07-06 編程語(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)有了
欄目分類
最近更新