接口服務(wù)設(shè)計(jì)中冪等性設(shè)計(jì)的理解,詳細(xì)分析冪等性設(shè)計(jì)的幾種實(shí)現(xiàn)方法

50cad3c088554ab6b7f621a4fb2e0ba3.jpg

什么是冪等性

  • 冪等性定義:
    • 一次和多次請求某一個資源對于資源本身應(yīng)該具有同樣的結(jié)果
    • 任意多次執(zhí)行對資源本身所產(chǎn)生的影響均與一次執(zhí)行的影響相同
  • 冪等性定義的幾個重點(diǎn):
    • 冪等不僅僅只是一次或者多次請求對資源沒有副作用
      • 比如,查詢數(shù)據(jù)庫操作,沒有增刪改,無論多少次操作對數(shù)據(jù)庫都沒有任何影響
    • 冪等還包括第一次請求的時候?qū)Y源產(chǎn)生了副作用,但是以后的多次請求都不會再對資源產(chǎn)生副作用
    • 冪等關(guān)注的是以后多次請求是否對資源產(chǎn)生副作用,并不關(guān)注結(jié)果
    • 網(wǎng)絡(luò)超時等問題,不是冪等的討論范圍
  • 冪等性是系統(tǒng)服務(wù)對外一種承諾,而不是實(shí)現(xiàn)
  • 承諾只要調(diào)用接口成功,外部多次調(diào)用對系統(tǒng)的影響是一致的
  • 聲明為冪等的服務(wù)會認(rèn)為外部調(diào)用失敗是常態(tài),并且失敗后必然會有重試

冪等性的使用場景

  • 業(yè)務(wù)開發(fā)中,經(jīng)常遇到重復(fù)提交的情況:
    • 由于網(wǎng)絡(luò)問題無法收到請求結(jié)果而重新發(fā)起請求
    • 前端的操作抖動而造成的重復(fù)提交的情況
  • 在交易系統(tǒng)中,支付系統(tǒng)這種重復(fù)提交造成的問題尤為明顯:
    • 用戶在APP上連續(xù)點(diǎn)擊多次提交訂單,后臺應(yīng)該只產(chǎn)生一個訂單
    • 向支付系統(tǒng)發(fā)起請求,由于網(wǎng)絡(luò)問題或者系統(tǒng)Bug問題導(dǎo)致重發(fā),支付系統(tǒng)應(yīng)該只做一次扣除操作
  • 聲明冪等的服務(wù)認(rèn)為,外部調(diào)用者會存在多次調(diào)用的情況,為了防止外部多次調(diào)用對系統(tǒng)的數(shù)據(jù)狀態(tài)發(fā)生多次改變,需要將服務(wù)設(shè)計(jì)為冪等

冪等和防重

  • 重復(fù)提交的情況和服務(wù)冪等的初衷是不同的
    • 重復(fù)提交是在第一次請求已經(jīng)成功的情況下 ,人為地進(jìn)行多次操作, 導(dǎo)致不滿足冪等要求的服務(wù)多次改變狀態(tài)
    • 冪等更多使用的情況是第一次請求因?yàn)槟承┣闆r,不如超時,而導(dǎo)致不知道結(jié)果或者請求失敗的異常情況下,發(fā)起多次請求
  • 冪等的目的是請求多次確認(rèn)第一次請求成功,不會因?yàn)槎啻握埱蠖霈F(xiàn)多次的狀態(tài)變化

保證冪等性的情況

  • 在SQL中,有以下三種場景,只有第三種場景需要保證冪等性:
    • SELECT col1 FROM tab1 WHERE col2=2 : 無論執(zhí)行多少次都不會改變狀態(tài),是天然的冪等
    • UPDATE tab1 SET col1=1 WHERE col2=2 : 無論執(zhí)行成功多少次狀態(tài)都是一致的,也是冪等操作
    • UPDATE tab1 SET col1=col1+1 WHERE col2=2: 每次執(zhí)行的結(jié)果都會發(fā)生變化,這種不是冪等的,要采取策略保證冪等性

設(shè)計(jì)冪等性服務(wù)

  • 冪等使得客戶端邏輯處理很簡單,但是服務(wù)端邏輯會很復(fù)雜
  • 滿足冪等性服務(wù)需要包含兩點(diǎn)邏輯:
    • 首先去查詢上一次的執(zhí)行狀態(tài),如果沒有則認(rèn)為是第一次請求
    • 在服務(wù)改變狀態(tài)的業(yè)務(wù)邏輯前保證防重復(fù)提交的邏輯

保證冪等策略

  • 冪等需要通過唯一的業(yè)務(wù)單號來保證:
    • 相同的業(yè)務(wù)單號,認(rèn)為是同一業(yè)務(wù)
    • 使用唯一的業(yè)務(wù)單號確保:后面多次相同業(yè)務(wù)單號的處理邏輯和執(zhí)行效果是一致的
  • 冪等實(shí)現(xiàn)示例-支付:
    • 先查詢訂單是否支付過
    • 如果已經(jīng)支付過,返回支付成功
    • 如果沒有支付,則進(jìn)行支付流程,修改訂單的狀態(tài)為已支付

防重復(fù)提交策略

  • 在保證冪等的策略中,執(zhí)行是分兩步執(zhí)行的,后面一步依賴上面一步的查詢結(jié)果,這樣就無法保證原子性
  • 無法保證原子性在高并發(fā)的情況下會存在問題:
    • 第二次請求在第一次請求的下一步訂單狀態(tài)沒有修改為"已支付狀態(tài)"時進(jìn)行
    • 為了解決這個問題 :將查詢和變更狀態(tài)操作加鎖,并將并行操作改為串行執(zhí)行
樂觀鎖
  • 如果只是更新已有的數(shù)據(jù),沒有必要對業(yè)務(wù)進(jìn)行加鎖
  • 設(shè)計(jì)表結(jié)構(gòu)時使用樂觀鎖,一般通過version來實(shí)現(xiàn)樂觀鎖:
    • 保證執(zhí)行效率
    • 保證冪等
  UPDATE tab1
  SET   col1=1,version=version+1
  WHERE version=#version# 

由于ABA問題會導(dǎo)致樂觀鎖存在失效的情況,只要保證version值自增就不會出現(xiàn)ABA的問題

防重表
  • 使用orderNo作為去重表中的唯一索引,每次請求都根據(jù)訂單號orderNo向去重表中插入一條數(shù)據(jù):
    • 第一次請求查詢訂單支付狀態(tài):
      • 訂單沒有支付
      • 進(jìn)行支付操作
      • 無論成功與否,執(zhí)行完成之后更新訂單的狀態(tài)為成功或失敗,刪除去重表中的數(shù)據(jù)
      • 后續(xù)訂單因?yàn)楸碇械奈ㄒ凰饕迦胧?返回操作失敗,直到第一次請求完成(成功或者失敗)
  • 防重表的作用是實(shí)現(xiàn)加鎖的功能
分布式鎖
  • 可以使用Redis分布式鎖代替防重表的功能
  • 示例:
    • 訂單發(fā)起支付請求
    • 支付系統(tǒng)會去Redis緩存中查詢是否存在該訂單Key
    • 如果不存在,向Redis中增加Key為訂單號
    • 查詢訂單支付是否已經(jīng)支付
    • 如果沒有則進(jìn)行支付,支付完成后刪除該訂單的Key
  • 通過Redis實(shí)現(xiàn)分布式鎖,只有這次訂單請求完成,下次請求才會進(jìn)來
  • 對比去重表,Redis分布式鎖將放并發(fā)做在緩存中,效率更高
  • 同一時間只能完成一次支付請求
token令牌
  • token令牌分為兩個階段:
    • 申請token階段:
      • 在進(jìn)入到提交訂單頁面之前,需要訂單系統(tǒng)根據(jù)用戶信息向支付系統(tǒng)發(fā)起一次申請token的請求
      • 支付系統(tǒng)將token保存到Redis緩存中,給支付階段使用
    • 支付階段:
      • 訂單系統(tǒng)獲取到申請的token, 發(fā)起支付請求,
      • 支付系統(tǒng)檢查Redis是否存在該token
        • 如果存在,表示第一次發(fā)起支付請求,刪除緩存中的token開始支付邏輯處理
        • 如果緩存中不存在,表示非法請求
支付緩沖區(qū)
  • 支付緩沖區(qū):
    • 將訂單的支付請求都快速地接收下來,是一個快速接收請求的緩沖管道
    • 使用異步任務(wù)處理管道中的數(shù)據(jù),過濾調(diào)掉重復(fù)的待支付的數(shù)據(jù)
  • 優(yōu)點(diǎn): 同步轉(zhuǎn)異步,高吞吐
  • 缺點(diǎn): 無法及時返回支付結(jié)果,需要后續(xù)監(jiān)聽支付結(jié)果的異步返回
冪等的不足:
- 冪等是為了簡化客戶端邏輯,但是增加了服務(wù)提供者的邏輯和成本
- 冪等的使用需要根據(jù)具體場景具體分析
- 增加了額外控制冪等的業(yè)務(wù)邏輯,復(fù)雜了業(yè)務(wù)功能
- 將并行的功能轉(zhuǎn)化為串行,降低了執(zhí)行效率
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容