推薦學習:Redis影片教學
#前言:介面冪等性
問題,對於開發人員來說,是一個跟語言無關的公共問題。對於某些使用者請求,在某些情況下是可能重複發送的,如果是查詢類操作並無大礙,但其中有些是涉及寫入操作的,一旦重複了,可能會導致很嚴重的後果,例如交易的介面如果重複請求可能會重複下單。介面冪等性是指使用者對於同一操作發起的一次請求或多次請求的結果是一致的,不會因為多次點擊而產生了副作用。
在HTTP/1.1中,對冪等性進行了定義。它描述了一次和多次請求某一個資源對於資源本身應該具有同樣的結果,即第一次請求的時候對資源產生了副作用,但是以後的多次請求都不會再對資源產生副作用。這裡的副作用是不會對結果產生破壞或產生不可預料的結果。也就是說,其任意多次執行對資源本身所產生的影響均與一次執行的影響相同。
這類問題多於介面的:
insert
操作,這種情況下多次要求,可能會產生重複資料。 update
操作,如果只是單純的更新數據,例如:update user set status=1 where id=1
,是沒有問題的。如果還有計算,例如:update user set status=status 1 where id=1
,這種情況下多次要求,可能會導致資料錯誤。 在介面呼叫時一般情況下都能正常回傳資訊不會重複提交,不過在遇見以下情況時可以就會出現問題,如:
本文討論的是如何在服務端優雅地統一處理這種接口冪等性情況,如何禁止用戶重複點擊等客戶端操作不在此次討論範圍。
冪等性是為了簡化客戶端邏輯處理,能放置重複提交等操作,但卻增加了服務端的邏輯複雜性與成本,其主要是:
所以在使用時候需要考慮是否引入冪等性的必要性,根據實際業務場景具體分析,除了業務上的特殊要求外,一般情況下不需要引入的介面冪等性。
冪等意味著一條請求的唯一性。不管是你哪個方案去設計冪等,都需要一個全域唯一的ID ,去標記這個請求是獨一無二的。
全域唯一性ID,我們要怎麼去生成呢?你可以回想下,資料庫主鍵Id怎麼產生的呢?
是的,我們可以使用UUID
,但是UUID的缺點比較明顯,它字串佔用的空間比較大,產生的ID過於隨機,可讀性差,而且沒有遞增。
我們也可以使用雪花演算法(Snowflake)
產生唯一性ID。
雪花演算法是一種產生分散式全域唯一ID的演算法,所產生的ID稱為Snowflake IDs
。這種演算法由Twitter創建,並用於推文的ID。
一個Snowflake ID有64位。
當然,全域唯一性的ID,還可以使用百度的Uidgenerator
,或是美團的Leaf
# 。
冪等處理的過程,說到底其實就是過濾一下已經收到的請求,當然,請求一定要有一個全域唯一的ID標記
哈。然後,要怎麼判斷請求是否之前收到過呢?把請求儲存起來,收到請求時,先查下儲存記錄,記錄存在就回傳上次的結果,不存在就處理請求。
一般的冪等處理就是這樣,如下:
可能會想到的是,只要請求有唯一的請求編號,那麼就能藉用Redis做這個去重——只要這個唯一請求編號在Redis存在,證明處理過,那麼就認為是重複的。
方案描述:
所謂唯一請求序號,其實就是每次向服務端請求時候附帶一個短時間內唯一不重複的序號,該序號可以是一個有序ID,也可以是一個訂單號,一般由下游生成,在呼叫上游服務端介面時附加該序號和用於認證的ID。
當上游伺服器收到請求資訊後拿取該序號和下游認證ID 進行組合,形成用於操作Redis 的Key,然後到Redis 中查詢是否存在對應的Key 的鍵值對,根據其結果:
適用操作:
使用限制:
主要流程:
服務端提供取得Token 的接口,該Token 可以是一個序號,也可以是一個分散式ID 或UUID 字串。
客戶端呼叫介面取得 Token,這時候服務端會產生一個 Token 字串。
然後將該字串存入 Redis 資料庫中,以該 Token 作為 Redis 的鍵(注意設定過期時間)。
將 Token 返回客戶端,客戶端拿到後應存到表單隱藏域。
客戶端執行提交表單時,把 Token 存入 Headers 中,執行業務請求帶上該 Headers。
服務端接收到請求後從 Headers 拿到 Token,然後根據 Token 到 Redis 中尋找該 key 是否存在。
服務端根據 Redis 中是否存該 key 進行判斷,如果存在就將該 key 刪除,然後正常執行業務邏輯。如果不存在就拋異常,傳回重複提交的錯誤訊息。
注意,在並發情況下,執行 Redis 尋找資料與刪除需要保證原子性,否則很可能在並發下無法保證冪等性。其實作方法可以使用分散式鎖定或使用 Lua 表達式註銷查詢與刪除操作。
推薦學習:Redis影片教學
以上是簡單聊聊Redis處理介面冪等性的兩種方案的詳細內容。更多資訊請關注PHP中文網其他相關文章!