冪等性設計
今天我們來聊聊接口的冪等性設計,所謂冪等,就是任意多次執行所產生的影響均與一次執行的影響相同。 冪等性接口是指可以使用相同參數重複執行,並能獲得相同結果的接口。這裏就不展開數學中的定義了,有興趣的可以自行google。
爲什麼接口需要冪等呢?
我們都知道,作爲接口的調用方,對於接口調用的結果,一般會返回成功、失敗和超時。對於成功和失敗,都是明確的狀態,調用放可以根據結果做相應的處理,但是對於超時,由於不確定是否成功請求了,作爲調用方來說,所以一般都會選擇重試。而重試就會出現定義中描述的多次執行。
可以從下面這個例子中加深一下理解:
創建訂單時,需要減庫存,如果減庫存接口超時了,調用方重新調用一次(無論是否成功的執行了減庫存代碼),應該要保證不會多減一次庫存。
要保證不會多件一次庫存,一般有兩種做法:
- 接口提供方需要提供相應的查詢接口。調用方在超時後去查詢一下是否成功。是否多扣一次庫存掌握在調用方手裏。如果接口是提供給第三方使用的,就會存在一定的風險。
- 接口支持冪等。這樣冪等的保證完全掌握在提供方自己手裏,完全不用擔心。
全局ID
要讓接口支持冪等,要怎麼做呢,你可能會想到在減庫存之前增加一次查詢,已經減過的直接返回不就完事了麼?這樣確實能達到目的,可是會額外多了一次查詢,有沒有什麼更優的方法呢?
要保證減庫存操作的唯一性,可以在接口上多加一個參數,這個參數必須全局唯一,數據庫設計表的時候這個字段要加上唯一索引,當多次保存相同數據的時候,數據庫就會報錯,這就證明了接口已經成功調用過,可以直接返回。
那這個全局ID由誰來分配呢?
-
可以創建一個分配中心,由中心統一分配。
優點:分配ID與業務集羣解耦。
缺點:需要單獨維護分配中心,這個分配中心也必須做成高可用集羣,增加維護成本。
-
集成在業務服務集羣。
優點:業務服務集羣本來就是高可用的,無需提供額外保證。
缺點:分配ID與業務耦合(這其實沒什麼影響),需要保證業務服務集羣生成ID的唯一性。
一般來說,後者是比較好的方案,我們只要提供一個能在集羣上生成全局唯一ID的算法即可。
除了保證全局唯一,最好具備以下特點(非必須):
- 遞增,起碼保證每臺機器上的ID遞增。(保證數據庫性能)
- 明確的規則,ID的各個位都有具體的定義。(方便追溯)
接下來就來說說現階段常用的全局ID算法。
UUID
UUID設計的目的就是讓分佈式系統中的所有元素,都能有唯一的辨識信息,而不需要通過中央控制端來做辨識信息的指定。關於UUID的設計原理可自行Google。
優點:實現簡單(大多數編程語言都集成到工具庫了),本地生成,性能好,擴展性高,不需要協定。
缺點:無法遞增(消耗數據庫性能)、UUID過長(消耗存儲空間)。
在中小型項目中,UUID會是不錯的選擇。 爲什麼這麼說呢?面對併發度不高的系統,數據庫性能一般不會達到瓶頸,所以說UUID是犧牲數據庫性能換取其優點的一種選擇。
Snowflake
Snowflake是Twitter 的開源項目,它生成的ID是64bit的正整數,結構如圖:
- 1bit:固定爲0,二進制中最高位爲符號位,0爲整數,1位負數。所以固定爲0表示生成的ID都爲正數
- 41bit:作爲毫秒數,大約能用69年。
- 10bit:作爲機器編號(5bit是數據中心ID,5bit爲機器ID)。支持1204個實例。
- 12bit:序列號,一毫秒最多生成2^12=4096個。
優點:遞增,且按時間有序。性能高,可根據情況分配bit。
缺點:依賴機器時鐘。在分佈式系統中,各個機器上的時間不可能完全一樣,在同步各機器的時間時,可能會造成重複ID。
在高併發的業務下,Snowflake生成的整數ID的存儲和讀取性能都要優於UUID。 現階段國內有很多基於Snowflake算法的特定實現,比如百度的UidGenerator。
關於Redis和MongoDB的設計這裏就不展開了。畢竟要強依賴於存儲系統,添加了維護成本和風險點。
業務邏輯
正如我們前面講過的,要依賴於數據庫唯一性約束,當數據庫報唯一性衝突時,就說明這個求情已經成功過了,不用再執行,直接返回即可。
HTTP的冪等性
這裏給出http請求的冪等性要求:
方法 | 冪等 | 描述 |
---|---|---|
GET | √ | 天然冪等 |
HEAD | √ | 天然冪等 |
OPTIONS | √ | 天然冪等 |
DELETE | √ | 天然冪等 |
PUT | √ | 天然冪等 |
POST | × | 需要支持冪等 |
對於POST方法,可能會出現多次提交的問題,比如由於網絡不好等原因,造成請求超時,這是用戶再點一次提交按鈕。對此一般的冪等性解決方法如下:
- 在提交的表單隱藏一個全局ID,這個全局ID需要提前向後端獲取,提交的時候把這個ID一起提交過來,按照上圖所描述的業務邏輯,來支持冪等。
- 後端成功以後前端跳轉,跳轉到GET請求,把剛纔提交的數據展示出來。
小結
這篇講了冪等性設計的要點,並給出了設計方案,大家可根據具體情況選擇合適的方案。