關於支付相關,訂單相關以及一些涉及費用的操作在業務上都是要求介面具有冪等性的。否則在高併發的場景下,同一筆交易請求多次,則會造成損失,這是不可忽視的錯誤。
例如一筆訂單,因為網路或者操作的原因,造成同時發起了兩次申請。
一般的介面設計中,對於重**起的交易都是先查詢是否存在這筆訂單,如果不存在,則繼續進行,並插入一條記錄,如果存在這筆交易,則返回訂單處理中。這種設計在不存在併發的情況下是沒有問題的。例子如下:
public
class
orderservice
// do something
return
"交易完成";}
public orderdata findorderandinsert
(string id)
// 不存在則插入資料
orderdata order =
neworderdata()
; order.
setid
(id)
;insertdata
(order)
;return null;
}}
但是在併發的場景下,兩筆交易同時呼叫介面findorderandinsert()方法,
a請求查詢訂單,發現不存在,則插入一條記錄,在插入這個操作還沒完成的時候,b請求進來,因為a的記錄還沒有插入資料庫,這個時候b查詢訂單,發現不存在,則也插入一條記錄,並繼續執行。如果兩條記錄沒有唯一索引的限制,則會完成兩筆交易。如果是支付交易,相當於我支付了兩次。所以這樣是不合理的。
冪等性:在程式設計中乙個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。冪等函式,或冪等方法,是指可以使用相同引數重複執行,並能獲得相同結果的函式。這些函式不會影響系統狀態,也不用擔心重複執行會對系統造成改變。例如,「settrue()」函式就是乙個冪等函式,無論多次執行,其結果都是一樣的.更複雜的操作冪等保證是利用唯一交易號(流水號)實現。
給findorderandinsert()方法加鎖,用關鍵字synchronized加鎖,來防止併發產生。但是這樣做,對於每一次請求都要加鎖,勢必會影響介面的效能。
public
synchronized orderdata findorderandinsert
(string id)
// 不存在則插入資料
orderdata order =
neworderdata()
; order.
setid
(id)
;insertdata
(order)
;return null;
}
是給流水表新增交易號為唯一索引,所有交易進來先插入一條資料,然後捕獲資料庫丟擲的主鍵衝突的異常,如果捕獲了異常則證明交易已經執行,則可以從資料庫中查出這條記錄的資訊返回給呼叫方。如果沒有捕獲到異常說明沒有重**起則繼續執行。
但是這樣的方法,我每乙個需要冪等性的介面都需要配套寫一套方法,明顯有些過於重複了。
public string execute
(orderdata data)
catch
(duplicatekeyexception e)
// do something
return
"交易完成"
;}
所有請求增加乙個token欄位,取值唯一。
設計乙個併發控制表,將請求token設定為唯一索引。
設計乙個併發控制註解,帶有該註解則認為是需要冪等性控制。
通過aop面向切面將切入點設定為併發控制註解,並通過環繞增強。
所有請求在呼叫介面之前先到併發控制表記錄一條流水號,方法介面呼叫結束再刪除這條記錄,防止資料過大。
// 所有請求的父物件
public
class
baserequest
public
void
settoken
(string token)
}// 具體介面請求物件類
public
class
orderdata
extends
baserequest
public
void
setid
(string id)
}/**
* 需要冪等控制的介面
*/@target()
@retention
(retentionpolicy.runtime)
@documented
public @inte***ce
repetitioncheck
@aspect
@component
public
class
aopservice
;@around
("pointcut()"
)public object interceptor
(proceedingjoinpoint pjp)
catch
(duplicatekeyexception e)
catch
(throwable throwable)
finally
return obj;}}
// 具體實現類
@repetitioncheck
public
class
orderservice
// do something
return
"交易完成";}
}
通過redis輔助,每次請求先在redis裡查詢是否存在key為token的資料,如果不存在,則將token放入到redis中,如果存在,這說明是重**起的交易,直接返回即可。因為redis是單執行緒的,所以是執行緒安全的。不存在併發問題。
@before
("pointcut()"
)public object interceptor1
(proceedingjoinpoint pjp)
// 插入一條資料redis ,並設定過期時間
redistemplate.
opsforvalue()
.set
(token,
"1",
5, timeunit.seconds)
; obj = pjp.
proceed()
;}catch
(throwable throwable)
return obj;
}
關於介面設計的冪等性
由於各種不可控因素導致同乙個介面使用相同的引數被呼叫了n次,而不影響其結果。1 查詢和刪除天然冪等 2 在插入操作中,可使用部分字段建立唯一索引或組合唯一來防止髒資料的產生 3 在前端操作時,可在開啟頁面時向伺服器申請乙個token令牌,使用一次之後刪除,用來防止重複提交 4 悲觀鎖 假設每次操作都...
介面冪等性設計
在系統中,乙個介面執行多次,與執行一次的效果是一致的 冪等性的核心思想 通過唯一的業務單號保證冪等 update自身帶鎖。直接update不會出現併發修改問題。樂觀鎖是先查詢在修改 update 商品表 set 庫存 庫存 購買量 version 查詢version值 1 where version...
介面冪等性
例如 在http協議中,get請求,會得到同樣的資料 bool get money id,amount 1引數 id 使用者的賬戶 amount 表示取多少錢 返回值 true 表示取錢成功 false 表示取錢失敗 情景一 1 一位使用者a 取100塊錢,這個請求,傳送到了伺服器 2 伺服器正常的...