共計 3634 個字符,預計需要花費 10 分鐘才能閱讀完成。
這篇文章主要介紹“Redis 處理接口冪等性的方案有哪些”,在日常操作中,相信很多人在 Redis 處理接口冪等性的方案有哪些問題上存在疑惑,丸趣 TV 小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Redis 處理接口冪等性的方案有哪些”的疑惑有所幫助!接下來,請跟著丸趣 TV 小編一起來學習吧!
前言:接口冪等性問題,對于開發人員來說,是一個跟語言無關的公共問題。對于一些用戶請求,在某些情況下是可能重復發送的,如果是查詢類操作并無大礙,但其中有些是涉及寫入操作的,一旦重復了,可能會導致很嚴重的后果,例如交易的接口如果重復請求可能會重復下單。接口冪等性是指用戶對于同一操作發起的一次請求或者多次請求的結果是一致的,不會因為多次點擊而產生了副作用。
一、接口冪等性 1.1、什么是接口冪等性
在 HTTP/1.1 中,對冪等性進行了定義。它描述了一次和多次請求某一個資源對于資源本身應該具有同樣的結果,即第一次請求的時候對資源產生了副作用,但是以后的多次請求都不會再對資源產生副作用。這里的副作用是不會對結果產生破壞或者產生不可預料的結果。也就是說,其任意多次執行對資源本身所產生的影響均與一次執行的影響相同。
這類問題多發于接口的:
insert 操作,這種情況下多次請求,可能會產生重復數據。
update 操作,如果只是單純的更新數據,比如:update user set status=1 where id=1,是沒有問題的。如果還有計算,比如:update user set status=status+1 where id=1,這種情況下多次請求,可能會導致數據錯誤。
1.2、為什么需要實現冪等性
在接口調用時一般情況下都能正常返回信息不會重復提交,不過在遇見以下情況時可以就會出現問題,如:
前端重復提交表單:在填寫一些表格時候,用戶填寫完成提交,很多時候會因網絡波動沒有及時對用戶做出提交成功響應,致使用戶認為沒有成功提交,然后一直點提交按鈕,這時就會發生重復提交表單請求。
用戶惡意進行刷單:例如在實現用戶投票這種功能時,如果用戶針對一個用戶進行重復提交投票,這樣會導致接口接收到用戶重復提交的投票信息,這樣會使投票結果與事實嚴重不符。
接口超時重復提交:很多時候 HTTP 客戶端工具都默認開啟超時重試的機制,尤其是第三方調用接口時候,為了防止網絡波動超時等造成的請求失敗,都會添加重試機制,導致一個請求提交多次。
消息進行重復消費:當使用 MQ 消息中間件時候,如果發生消息中間件出現錯誤未及時提交消費信息,導致發生重復消費。
本文討論的是如何在服務端優雅地統一處理這種接口冪等性情況,如何禁止用戶重復點擊等客戶端操作不在此次討論范圍。
1.3、引入冪等性后對系統的影響
冪等性是為了簡化客戶端邏輯處理,能放置重復提交等操作,但卻增加了服務端的邏輯復雜性和成本,其主要是:
把并行執行的功能改為串行執行,降低了執行效率。
增加了額外控制冪等的業務邏輯,復雜化了業務功能;
所以在使用時候需要考慮是否引入冪等性的必要性,根據實際業務場景具體分析,除了業務上的特殊要求外,一般情況下不需要引入的接口冪等性。
二、如何設計冪等
冪等意味著一條請求的唯一性。不管是你哪個方案去設計冪等,都需要一個全局唯一的 ID,去標記這個請求是獨一無二的。
如果你是利用唯一索引控制冪等,那唯一索引是唯一的
如果你是利用數據庫主鍵控制冪等,那主鍵是唯一的
如果你是悲觀鎖的方式,底層標記還是全局唯一的 ID
2.1、全局的唯一性 ID
全局唯一性 ID,我們怎么去生成呢?你可以回想下,數據庫主鍵 Id 怎么生成的呢?
是的,我們可以使用 UUID,但是 UUID 的缺點比較明顯,它字符串占用的空間比較大,生成的 ID 過于隨機,可讀性差,而且沒有遞增。
我們還可以使用雪花算法(Snowflake)生成唯一性 ID。
雪花算法是一種生成分布式全局唯一 ID 的算法,生成的 ID 稱為 Snowflake IDs。這種算法由 Twitter 創建,并用于推文的 ID。
一個 Snowflake ID 有 64 位。
第 1 位:Java 中 long 的最高位是符號位代表正負,正數是 0,負數是 1,一般生成 ID 都為正數,所以默認為 0。
接下來前 41 位是時間戳,表示了自選定的時期以來的毫秒數。
接下來的 10 位代表計算機 ID,防止沖突。
其余 12 位代表每臺機器上生成 ID 的序列號,這允許在同一毫秒內創建多個 Snowflake ID。
當然,全局唯一性的 ID,還可以使用百度的 Uidgenerator,或者美團的 Leaf。
2.2、冪等設計的基本流程
冪等處理的過程,說到底其實就是過濾一下已經收到的請求,當然,請求一定要有一個全局唯一的 ID 標記哈。然后,怎么判斷請求是否之前收到過呢?把請求儲存起來,收到請求時,先查下存儲記錄,記錄存在就返回上次的結果,不存在就處理請求。
一般的冪等處理就是這樣,如下:
三、接口冪等性常見解決方案 3.1、下游傳遞唯一請求編號
可能會想到的是,只要請求有唯一的請求編號,那么就能借用 Redis 做這個去重——只要這個唯一請求編號在 Redis 存在,證明處理過,那么就認為是重復的。
方案描述:
所謂唯一請求序列號,其實就是每次向服務端請求時候附帶一個短時間內唯一不重復的序列號,該序列號可以是一個有序 ID,也可以是一個訂單號,一般由下游生成,在調用上游服務端接口時附加該序列號和用于認證的 ID。
當上游服務器收到請求信息后拿取該 序列號 和下游 認證 ID 進行組合,形成用于操作 Redis 的 Key,然后到 Redis 中查詢是否存在對應的 Key 的鍵值對,根據其結果:
如果存在,就說明已經對該下游的該序列號的請求進行了業務處理,這時可以直接響應重復請求的錯誤信息。
如果不存在,就以該 Key 作為 Redis 的鍵,以下游關鍵信息作為存儲的值(例如下游商傳遞的一些業務邏輯信息),將該鍵值對存儲到 Redis 中,然后再正常執行對應的業務邏輯即可。
適用操作:
插入操作
更新操作
刪除操作
使用限制:
要求第三方傳遞唯一序列號;
需要使用第三方組件 Redis 進行數據效驗;
主要流程:
主要步驟:
下游服務生成分布式 ID 作為序列號,然后執行請求調用上游接口,并附帶“唯一序列號”與請求的“認證憑據 ID”。
上游服務進行安全效驗,檢測下游傳遞的參數中是否存在“序列號”和“憑據 ID”。
上游服務到 Redis 中檢測是否存在對應的“序列號”與“認證 ID”組成的 Key,如果存在就拋出重復執行的異常信息,然后響應下游對應的錯誤信息。如果不存在就以該“序列號”和“認證 ID”組合作為 Key,以下游關鍵信息作為 Value,進而存儲到 Redis 中,然后正常執行接來來的業務邏輯。
上面步驟中插入數據到 Redis 一定要設置過期時間。這樣能保證在這個時間范圍內,如果重復調用接口,則能夠進行判斷識別。如果不設置過期時間,很可能導致數據無限量的存入 Redis,致使 Redis 不能正常工作。
3.2、防重 Token 令牌
方案描述:
針對客戶端連續點擊或者調用方的超時重試等情況,例如提交訂單,此種操作就可以用 Token 的機制實現防止重復提交。簡單的說就是調用方在調用接口的時候先向后端請求一個全局 ID(Token),請求的時候攜帶這個全局 ID 一起請求(Token 最好將其放到 Headers 中),后端需要對這個 Token 作為 Key,用戶信息作為 Value 到 Redis 中進行鍵值內容校驗,如果 Key 存在且 Value 匹配就執行刪除命令,然后正常執行后面的業務邏輯。如果不存在對應的 Key 或 Value 不匹配就返回重復執行的錯誤信息,這樣來保證冪等操作。
使用限制:
需要生成全局唯一 Token 串;
需要使用第三方組件 Redis 進行數據效驗;
主要流程:
服務端提供獲取 Token 的接口,該 Token 可以是一個序列號,也可以是一個分布式 ID 或者 UUID 串。
客戶端調用接口獲取 Token,這時候服務端會生成一個 Token 串。
然后將該串存入 Redis 數據庫中,以該 Token 作為 Redis 的鍵(注意設置過期時間)。
將 Token 返回到客戶端,客戶端拿到后應存到表單隱藏域中。
客戶端在執行提交表單時,把 Token 存入到 Headers 中,執行業務請求帶上該 Headers。
服務端接收到請求后從 Headers 中拿到 Token,然后根據 Token 到 Redis 中查找該 key 是否存在。
服務端根據 Redis 中是否存該 key 進行判斷,如果存在就將該 key 刪除,然后正常執行業務邏輯。如果不存在就拋異常,返回重復提交的錯誤信息。
注意,在并發情況下,執行 Redis 查找數據與刪除需要保證原子性,否則很可能在并發下無法保證冪等性。其實現方法可以使用分布式鎖或者使用 Lua 表達式來注銷查詢與刪除操作。
到此,關于“Redis 處理接口冪等性的方案有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注丸趣 TV 網站,丸趣 TV 小編會繼續努力為大家帶來更多實用的文章!