在業務設計中防重設計是一個關鍵點,以接口設計為例,防重就是防止接口被多次調用而產生臟數據,比如支付訂單出現重復支付,所以說防重至關重要,在如何防重之前我們首先看一下是如何出現重復請求的。
何時出現多次調用。
多次調用接口的出現有主觀原因比如:人為的重復請求攻擊,用戶的誤操作等;也有客觀原因比如:為了健壯性進行超時重試;
- 重復請求攻擊。
對于這種惡意攻擊,其實已經屬于安全范疇了,我們可以通過黑名單 + 限流來處理,下一步再考慮防重處理。
- 用戶的誤操作。
比如用戶在界面點擊提交按鈕,因為手誤出現多次點擊提交,這種情況客戶端可以做一些處理,減少用戶的誤操作,比如提交完按鈕變灰等;當然提供方的防重處理也是必不可少的。
- 超時重試。
接口調用方為了保證系統的健壯性,往往會做一些重試處理,比如各種 RPC 框架都已經幫我們內置了容錯機制,提供方需要做好防重。
如何做好防重處理,其實核心就是保證多個相同的請求只有一次被執行,或者說多次調用和一次調用產生的效果是一樣的,也就是我們經常說的要保證冪等性。
何時需要防重。
所有的操作說到底都是增刪改查,其實我們真正需要做防重處理的,更重要的是增加和修改;查詢和刪除本身執行一次和多次,產生的效果是一樣的,有天然冪等性,雖然說有天然冪等性,但是查詢和刪除本身也是要消耗資源的,如果能防止重復執行,也能節省資源。
增加和修改是必須要做防重的,增加可以以下單為例,修改可以以更新庫存為例,如果沒有做好防重后果是非常嚴重的;下面具體看看都有哪些防重的措施。
如何防重?
上面說到主要是針對增加和修改需要做好防重處理,當然針對增加和修改其實是有不同的防重措施,也有統一的方式,下面分別介紹:
統一防重。
- token 機制。
服務器需要提供獲取 token 機制的服務,這樣每次客戶端請求的時候先獲取 token,服務器端會將 token 保存在 redis 中;客戶端發送請求的時候會帶上 token,這樣服務器端可以拿到 token 直接去 redis 中做刪除處理,根據返回值判斷是否成功。
- 防重表。
可以利用表的唯一索引約束,可以使用類似防重 key 作為唯一索引字段,多次請求過來只會有一個插入成功,為了防止防重表數據過多,可以啟動一個定時器定時清理;
以上幾種方式其實和具體的業務關系不大,可以適用大部分場景;而且通過 redis 或者數據庫,以及原子性操作來保證在分布式環境下也可以很好的運行;
插入防重。
- select+insert。
插入數據最先想到就是先檢查有沒有,然后再插入,但是這樣明顯存在兩個操作,不是原子性操作,單節點下還能通過鎖來解決,分布式環境下就需要用到分布式鎖來保證原子性了;當然也可以結合其他方式一起使用,比如下面的唯一主鍵機制。
- 唯一主鍵。
這種方式其實就是不直接使用數據庫的自增主鍵了,使用分布式 id 算法生成,這樣在插入數據的時候就可以通過唯一主鍵來進行約束,保證只會有一條成功。
- 回滾機制。
有些業務其實是有正向流程和逆向流程,比如支付訂單,在接收到銀行返回的支付成功通知時判斷訂單的狀態,如果已經支付成功,可以直接走退款流程。
防重的方法很多,我們往往需要更加自己的業務做相關的選擇,不同的業務,不同的業務量,不同的容忍程度都會影響我們如何去做防重;每種方式也不是都是獨立存在的,有時候往往需要多種方式整合起來。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.