要在應用中做到冪等,其實并不難,本文嘗試做一個系統性的總結,歡迎一起探討,
什么是冪等
某個操作執行一次,跟執行多次的效果一樣,冪等一詞來自于數學中的冪等,即f(f(x)) = f(x),
需要保證冪等的場景
查詢類的讀操作,天然是冪等的,多次呼叫不會有副作用,需考慮以下幾種寫操作的情況:
- 呼叫下游寫介面
- 寫資料庫、寫Redis等
- 訊息訂閱和處理
例子:不能給用戶重復發放優惠券、現金獎勵、通知等,商家更新商品時不能重復增加或減少庫存,
下面分別討論這幾種情況,
1、呼叫下游寫介面
主要依靠下游服務保證冪等,
本服務能做的是,在調下游寫介面時不做重試,需設定重試次數為0,
2、自己服務保證
2.1 基于狀態的冪等
這種情況比較簡單,只有當滿足前置條件時才允許操作,否則不允許更新(例如已經是終態),直接回傳,
例子:訂單支付成功后,不允許重復支付,
2.2 基于唯一鍵的冪等
冪等key的選取
與業務強相關,可以是商品id、訂單id、用戶id,或者日期等,或者是幾個業務欄位的組合,
幾個例子:
- 一個用戶每天只能領一張優惠券,通過 用戶id+優惠券型別+日期字串 即可唯一標識
- B端更新庫存,商品id+該商品的版本號
- C端扣庫存,訂單id
值得注意的是,需要區分新增和修改:修改時的冪等key往往需要帶上版本號,才能區分是否同一次修改,每次修改對應一個唯一的版本號,
實作方式
MySQL表中為冪等key建立唯一索引:強冪等,例如資金、訂單,絕對不允許重復處理,當插入重復資料時報錯,
不推薦用Redis實作冪等,一旦Redis出問題,比如節點宕機,可能出現2個client同時獲取到鎖的情況,
MySQL冪等偽代碼:
插入重復記錄,捕獲例外,提示冪等攔截,
try {
// 插入記錄
someDao.create(someRecord);
} catch (DataIntegrityViolationException e) {
// 如果是重復記錄,回傳例外
return failResponse("冪等攔截");
} catch (Throwable t) {
// 例外處理
return failResponse("其他例外");
}
3、訊息訂閱和處理
MQ通常會保證訊息至少發送一次(可能多次),并且在機器實體重啟或發版時,consumer group會做rebalance,進而收到重復的訊息,因此,訊息的冪等處理必不可少,
實作方式:
在處理訊息前加上Redis鎖:如果上鎖成功,則繼續處理,否則稍后重試,
- setnxex,不存在時才設定,時效即為鎖的租期,否則忽略
- 接下來的業務處理,如果是自身邏輯需要強冪等則使用上述資料庫冪等方式,如果全部依賴下游則依賴下游實作冪等
Redis冪等偽代碼:
// 生成冪等key
String redisKey = buildRedisKey();
// 上Redis鎖,租期為leaseTime
if (redisLock.tryLock(redisKey, leaseTime)) {
// 業務邏輯處理
} else {
// 稍后重試
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/413141.html
標籤:其他
上一篇:服務探活的五種方式
