引子
羣裏發了一個總共1千元的拼手氣紅包,共10個。靜兒點進去,額,搶到了0.05元。這個不甘心啊。退出來從新打開了這個紅包,你猜怎樣?顯示我搶到了0.05元!
這就是冪等(idempotence),無論多少次請求某一個資源,對資源都具備相同的影響。冪等性是系統的接口對外一種承諾,承諾只要調用接口成功,外部屢次調用對系統只產生一次反作用。
爲何要冪等
世界上最遙遠的距離是我終於鼓起勇氣,對着馬路對面的你大喊:「你願意娶我嗎?」我看到你面帶燦爛的笑容,正回答的時候……一輛大卡車駛過,你的回答我沒有聽見。
因各類不可抗因素產生的沒有收到響應,一個簡單有效的方法就是重試。被重試的接口必須是冪等的。
冪等性是分佈式系統設計中的一個重要概念,對超時處理、系統恢復等具備重要意義。
保證冪等的手段
保證冪等須要理清楚兩件事情:冪等條件和指望結果。
你們可能據說過保證冪等的手段有token令牌、分佈式鎖、去重表、數據庫惟一索引等。這些所謂的冪等手段實際上防重手段。防重本質是防止一個相同的請求被當成多個不一樣的請求來處理。冪等的條件是知道這是一個相同的請求。防重和冪等本質上是兩個不一樣的階段。
狀態機冪等
在支付場景中,建立了一個支付訂單,發起了一個支付請求,這個訂單不論多少次重複請求,都應該保證最多隻扣款一次。即
相同支付訂單ID(冪等條件) —> 最多一次扣款(指望結果)
爲了實現這個目標,能夠考慮使用有限狀態機。
有限狀態機(Finite-state machine FSM),又稱有限狀態自動機,簡稱狀態機,是表示有限個狀態以及在這些狀態之間的轉移和動做等行爲的數學模型。用於處理複雜的狀態轉換。
在這個支付的例子中,爲了化簡,不考慮退款、取消訂單等複雜的狀態,只考慮未支付和已支付兩種狀態之間的轉換。
由上面的狀態轉換圖能夠看到,相同支付訂單ID從未支付狀態,要不就是支付不成功停留在未支付狀態,要不就是支付成功,狀態轉移爲已支付。此狀態轉移過程不可逆。
public enum OrderStateEnum {
UNPAID {
@Override
public OrderStateEnum changeState() {
if (doPay()) {
return PAID;
}
return UNPAID;
}
},
PAID {
@Override
public OrderStateEnum changeState() {
return PAID;
}
};
public abstract OrderStateEnum changeState();
public boolean doPay() {
//這裏是邏輯僞代碼,能夠是發起下游調用請求支付通道等
return true;
}
}
這是一個java版本的簡單狀態機實現。狀態機裏定義了一個未支付狀態和其行爲changeState。changeState又定義了一個未支付狀態和其行爲changeState。
利用狀態機來實現這個冪等支付請求的設計流程圖以下:
參考狀態機實現和上圖可知,相同支付ID的請求,支付狀態只能進行一次從未支付到已支付的轉換。從而保證了其冪等性。
按目標冪等
先來回答一個小學生的問題:
定了一個會議,參加人數爲10人。發現會議室的椅子只有5把。3個提早來到會議室的同窗熱心的去其餘地方搬椅子進來。問:每人要搬幾把椅子?
有人要說這不是把簡單的問題複雜了嗎?你們看到椅子不夠就去搬,看夠10把椅子了就不搬就能夠了。對了,這實際上是一個很好的解題思路,徹底能夠用在設計當中,就是按目標冪等。
相同會議ID(冪等條件) —> 總數10把椅子(指望結果)
利用按目標冪等來實現這個總數10把椅子請求的設計流程圖以下:
採用按目標的設計,相同會議ID,無論多少次請求,請求椅子的總數就是10把。屢次請求不改變行爲,從而實現了冪等。
總結
支持冪等是一個接口的基本素養