看一下維基百科怎麼說的:前端
冪等性: 屢次調用方法或者接口不會改變業務狀態,能夠保證重複調用的結果和單次調用的結果一致。mysql
用戶註冊,用戶建立商品等操做,前端都會提交一些數據給後臺服務,後臺須要根據用戶提交的數據在數據庫中建立記錄。若是用戶不當心多點了幾回,後端收到了好幾回提交,這時就會在數據庫中重複建立了多條記錄。這就是接口沒有冪等性帶來的 bug。redis
對於給第三方調用的接口,有可能會由於網絡緣由而調用失敗,這時,通常在設計的時候會對接口調用加上失敗重試的機制。若是第一次調用已經執行了一半時,發生了網絡異常。這時再次調用時就會由於髒數據的存在而出現調用異常。sql
在使用消息中間件來處理消息隊列,且手動 ack 確認消息被正常消費時。若是消費者忽然斷開鏈接,那麼已經執行了一半的消息會從新放回隊列。數據庫
當消息被其餘消費者從新消費時,若是沒有冪等性,就會致使消息重複消費時結果異常,如數據庫重複數據,數據庫數據衝突,資源重複等。後端
經過 token 機制實現接口的冪等性,這是一種比較通用性的實現方法。markdown
示意圖以下:網絡
具體流程步驟:ui
客戶端會先發送一個請求去獲取 token,服務端會生成一個全局惟一的 ID 做爲 token 保存在 redis 中,同時把這個 ID 返回給客戶端spa
客戶端第二次調用業務請求的時候必須攜帶這個 token
服務端會校驗這個 token,若是校驗成功,則執行業務,並刪除 redis 中的 token
若是校驗失敗,說明 redis 中已經沒有對應的 token,則表示重複操做,直接返回指定的結果給客戶端
注意:
對 redis 中是否存在 token 以及刪除的代碼邏輯建議用 Lua 腳本實現,保證原子性
全局惟一 ID 能夠用百度的 uid-generator、美團的 Leaf 去生成
這種實現方式是利用 mysql 惟一索引的特性。
示意圖以下:
具體流程步驟:
創建一張去重表,其中某個字段須要創建惟一索引
客戶端去請求服務端,服務端會將此次請求的一些信息插入這張去重表中
由於表中某個字段帶有惟一索引,若是插入成功,證實表中沒有此次請求的信息,則執行後續的業務邏輯
若是插入失敗,則表明已經執行過當前請求,直接返回
這種實現方式是基於 SETNX 命令實現的
SETNX key value:將 key 的值設爲 value ,當且僅當 key 不存在。若給定的 key 已經存在,則 SETNX 不作任何動做。
該命令在設置成功時返回 1,設置失敗時返回 0。
具體流程步驟:
客戶端先請求服務端,會拿到一個能表明此次請求業務的惟一字段
將該字段以 SETNX 的方式存入 redis 中,並根據業務設置相應的超時時間
若是設置成功,證實這是第一次請求,則執行後續的業務邏輯
若是設置失敗,則表明已經執行過當前請求,直接返回
這幾種實現冪等的方式其實都是大同小異的,相似的還有使用狀態機、悲觀鎖、樂觀鎖的方式來實現,都是比較簡單的。
總之,當你去設計一個接口的時候,冪等都是首要考慮的問題,特別是當你負責設計轉帳、支付這種涉及到 money 的接口,你要格外注意嘍!