概述
所謂接口冪等性就是:在特定場景下,同一條件的多次接口調用,保證操作只執行一次,如果接口沒有保證冪等性,在以下場景就會產生問題
- 前端重複提交:用戶進行註冊、創建個人信息等操作,由於網絡抖動導致頁面沒有及時響應,用戶認爲沒有成功而多次點擊提交按鈕,發生重複提交表單請求
- 接口超時重試:提供外部系統調用的接口,因爲網絡抖動等原因執行成功但沒能及時響應,外部系統發起重試,導致重複調用
- 消息重複消費:使用消息中間件時,消費者手動 ack 確認消息被正常消費時,消費者突然斷開連接,已經執行的消息會重新放回隊列,被其他消費者重新消費
如何實現接口冪等性?
1. 防重 Token 令牌
具體流程如下:
- 客戶端獲取 token,服務端將 token 保存在 redis 中,token 需保證全局唯一
- 之後客戶端發起請求時必須攜帶 token
- 服務端校驗 token,如果成功則執行業務,並刪除 redis 中的 token,否則爲重複操作,直接返回結果
這種方式需保證同一請求都攜帶同一 token,比如同一訂單的支付操作都使用同一 token 請求。另外,在併發情況下,Redis 查找數據與刪除需要保證原子性,可以使用或 Lua 腳本保證
2. 使用 Redis 實現
這種實現方式是基於 redis 的 setnx 命令實現的,作用是如果 key 不存在,將 key 賦值爲 value 並返回 1,若 key 已存在,則不做操作並返回 0
具體流程如下:
- 客戶端請求服務端,將能代表這次請求的唯一標識以 setnx 的方式存入 redis,並根據業務設置相應的超時時間
- 如果設置成功,代表是第一次請求,執行後續業務邏輯
- 如果設置失敗,代表已經執行過請求,直接返回
redis 是單線程的,所以不會有併發問題
3. 加鎖實現
具體流程如下:
- 客戶端請求服務端,對能代表這次請求的唯一標識加鎖,保證同一時刻同一請求只有一個能被執行
- 服務端根據唯一標識判斷是否第一次請求,比如查詢數據庫是否已存在該唯一標識的記錄,或者該唯一標識對應記錄狀態是否爲【已完成】
- 如果是第一次請求,執行後續業務邏輯,否則直接返回
4. 冪等表
加鎖會影響性能,我們可以建一張冪等表,爲能代表這次請求的唯一標識建立唯一約束
具體流程如下:
- 客戶端請求服務端,服務端會將本次請求的信息插入冪等表
- 因爲有唯一約束,如果冪等表中不存在本次請求記錄,則插入成功,執行後續業務邏輯,插入失敗,則直接返回
使用這種方式,每次請求都會冪等表新建一條記錄,爲了避免表數據過大,可以定期進行清理,或者使用流水錶來代替冪等表。使用插入而不是查詢,也能有效避免併發問題