接口冪等設計方案

? ? ? ?最近一直在思考怎么保障接口調(diào)用的冪等性阐滩,經(jīng)過參考網(wǎng)上的一些資料結合自身的情況而有所得忧饭,現(xiàn)整理如下,做個備忘鳞骤,有興趣或者有同樣需求的朋友希望可以借此找到適合你們的方法窒百。

一、什么是接口冪等性

首先看下從網(wǎng)上扒了一段對冪等性的概念描述:

冪等性原本是數(shù)學上的概念豫尽,用在接口上就可以理解為:同一個接口篙梢,多次發(fā)出同一個請求,必須保證操作只執(zhí)行一次美旧。?調(diào)用接口發(fā)生異常并且重復嘗試時渤滞,總是會造成系統(tǒng)所無法承受的損失,所以必須阻止這種現(xiàn)象的發(fā)生榴嗅。

例如下面這些情況妄呕,如果沒有實現(xiàn)接口冪等性會有很嚴重的后果:

1、 支付接口嗽测,重復支付會導致多次扣錢 绪励;

2、訂單接口,同一個訂單可能會多次創(chuàng)建优炬。

二颁井、為什么會產(chǎn)生接口冪等性問題

什么情況下,會產(chǎn)生接口冪等性的問題呢蠢护?下面列舉了各種有可能產(chǎn)生接口冪等性問題的場景雅宾。

1、網(wǎng)絡波動, 可能會引起重復請求

2葵硕、用戶在操作時候可能會無意觸發(fā)多次下單交易,甚至沒有響應而有意觸發(fā)多次交易應用

3眉抬、使用了失效或超時重試機制(Nginx重試、RPC重試或業(yè)務層重試等)

4懈凹、頁面重復刷新

5蜀变、使用瀏覽器后退按鈕重復之前的操作,導致重復提交表單

6、使用瀏覽器歷史記錄重復提交表單

8介评、定時任務重復執(zhí)行

9库北、用戶雙擊提交按鈕

三、如何保證接口冪等性们陆?

那么寒瓦,如何保證接口冪等性呢?

解決辦法一般分為兩個方向坪仇,一個方向是客戶端防止重復調(diào)用杂腰,一個是服務端進行校驗。

1椅文、客戶端防止重復調(diào)用

? ? ?a喂很、 按鈕只可操作一次

? ? ? ? ??提交后把按鈕置灰或loding狀態(tài),消除用戶因為重復點擊而產(chǎn)生的重復記錄,但是客戶端防止重復提交并不是絕對可靠的皆刺,優(yōu)點是實現(xiàn)起來比較簡單少辣。

? ? b、使用Post/Redirect/Get模式

? ? ? ? ?在提交后執(zhí)行頁面重定向,這就是所謂的Post-Redirect—Get(PRG)模式,簡單來說就是當用戶提交連表單后,跳轉到一個重定向的信息頁面,這樣就避免用戶按F5刷新導致的重復提交,而且也不會出現(xiàn)瀏覽器表單重復提交的警告,也能消除按瀏覽器前進和后退導致同樣重復提交的問題芹橡。

2毒坛、服務端防止重復調(diào)用

a、token機制

功能上允許重復提交,但要保證重復提交不產(chǎn)生副作用,比如點擊n次只產(chǎn)生一條記錄,具體實現(xiàn)就是進入頁面時申請一個token,然后后面所有的請求都帶上這個token,后端根據(jù)token來避免重復請求林说。改方案需要客戶端先請求token接口煎殷,然后再調(diào)業(yè)務接口,調(diào)用流程比較復雜腿箩,流程圖如下:

b豪直、使用唯一索引防止新增臟數(shù)據(jù)

利用數(shù)據(jù)庫唯一索引機制,當數(shù)據(jù)重復時,插入數(shù)據(jù)庫會拋出異常,保證不會出現(xiàn)臟數(shù)據(jù)。但不適用于數(shù)據(jù)庫分片的場景

c珠移、樂觀鎖

如果更新已有數(shù)據(jù),可以進行加鎖更新,也可以設計表結構時使用樂觀鎖,通過version來做樂觀鎖,這樣既能保證執(zhí)行效率,又能保證冪等, 樂觀鎖的version版本在更新業(yè)務數(shù)據(jù)要自增

update table set version = version + 1 where id = #{id} and version = #{version}

示例: 當有重復請求的時候,第一個請求會獲取當前商品的version版本號,得到的version為1,緊接著由于第一個請求還沒更新商品的version,第二個請求獲取的version依然也是1, 這時候第一個請求操作更新的時候帶上version并作為條件并且自增更新,這時候商品的version就會變成2,當?shù)诙€請求去操作更新的時候明顯version不一致導致更新失敗弓乙。

select + insert or update or delete

該方案就是操作之前先查詢一下,符合要求再插入,該方案在沒有并發(fā)的系統(tǒng)中可以解決冪等問題末融,在單JVM有并發(fā)的時候可以用JVM加鎖來保證冪等性,在分布式環(huán)境它是無法保證冪等性,可以使用分布式來保證。

d暇韧、分布式鎖

如果是分布是系統(tǒng)勾习,構建全局唯一索引比較困難,例如唯一性的字段沒法確定懈玻,這時候可以引入分布式鎖巧婶,通過第三方的系統(tǒng)(redis或zookeeper),在業(yè)務系統(tǒng)插入數(shù)據(jù)或者更新數(shù)據(jù)涂乌,獲取分布式鎖艺栈,然后做操作,之后釋放鎖湾盒,這樣其實是把多線程并發(fā)的鎖的思路湿右,引入多多個系統(tǒng),也就是分布式系統(tǒng)中得解決思路罚勾。要點:某個長流程處理過程要求不能并發(fā)執(zhí)行毅人,可以在流程執(zhí)行之前根據(jù)某個標志(用戶ID+后綴等)獲取分布式鎖,其他流程執(zhí)行時獲取鎖就會失敗荧库,也就是同一時間該流程只能有一個能執(zhí)行成功堰塌,執(zhí)行完成后赵刑,釋放分布式鎖(分布式鎖要第三方系統(tǒng)提供)分衫。

e、狀態(tài)機冪等

在設計單據(jù)相關的業(yè)務般此,或者是任務相關的業(yè)務蚪战,肯定會涉及到狀態(tài)機(狀態(tài)變更圖),就是業(yè)務單據(jù)上面有個狀態(tài)铐懊,狀態(tài)在不同的情況下會發(fā)生變更邀桑,一般情況下存在有限狀態(tài)機,這時候科乎,如果狀態(tài)機已經(jīng)處于下一個狀態(tài)壁畸,這時候來了一個上一個狀態(tài)的變更,理論上是不能夠變更的茅茂,這樣的話捏萍,保證了有限狀態(tài)機的冪等。注意:訂單等單據(jù)類業(yè)務空闲,存在很長的狀態(tài)流轉令杈,一定要深刻理解狀態(tài)機,對業(yè)務系統(tǒng)設計能力提高有很大幫助 碴倾。

f逗噩、防重表

使用唯一主鍵去做防重表的唯一索引,比如使用訂單號作為防重表的唯一索引,每一次請求都根據(jù)訂單號向防重表中插入一條數(shù)據(jù),插入成功說明可以處理后面的業(yè)務,當處理完業(yè)務邏輯之后刪除防重表中的訂單號數(shù)據(jù),后續(xù)如果有重復請求,則會因為防重表唯一索引原因導致插入失敗,直接返回操作失敗,直到第一次請求返回結果,可以看出防重表作用就是加鎖的功能掉丽。

g、緩沖隊列

將請求都快速地接收下來后放入緩沖隊列中,后續(xù)使用異步任務處理隊列中的數(shù)據(jù),過濾掉重復的請求,該解決方案優(yōu)點是同步處理改成異步處理异雁、高吞吐量,缺點則是不能及時地返回請求結果,需要后續(xù)輪詢得處理結果捶障。

總的來講接口冪等的解決方案眾多,但個人覺得實現(xiàn)成本比較大纲刀,不利于落地残邀。

四、我的設計方案

1柑蛇、首先寫個注解UnicornIdempotent

這里group屬性對接口冪等進行分組芥挣,默認為unicorn

value屬性支持spel,用來計算接口請求唯一key

throwException屬性設置是否需要將重復提交的異常拋出來反饋給客戶端

2耻台、再來一個Aspect對接口進行攔截

大體上流程如下:

a空免、計算冪等的唯一標識 key

b、嘗試給這個key加鎖盆耽,此處用redis實現(xiàn)的分布式鎖

c蹋砚、根據(jù)加鎖的情況判斷是否是重復提交

d、如果加鎖失敗是重復提交則判斷是直接拋出異常還是返回上一次調(diào)用的緩存結果

e摄杂、如果加鎖成功則為正常請求坝咐,執(zhí)行具體的業(yè)務方法之后緩存調(diào)用結果到redis中

流程圖如下:

3、使用

?a析恢、在需要冪等的接口方法上增加UnicornIdempotent注解

b墨坚、配置文件

timeWindowSecond用來配置冪等支撐的時間窗口,超過時間窗口接口不冪等映挂,可以根據(jù)自己的實際情況來設置

tips用來配置拋出異常對客戶端的提示信息

4泽篮、總結

?a、該方法易于實現(xiàn)柑船,但是存在一個時間窗口帽撑,在時間窗口內(nèi)可以保證冪等,時間窗口外不保證冪等性鞍时,這個時間窗口期需要根據(jù)自己的實際情況來設定

b亏拉、需要接口設計時能夠提煉出接口調(diào)用的唯一key,需要開發(fā)人員根據(jù)業(yè)務來定逆巍,此處如果沒有指定唯一key及塘,則默認將請求URL+請求參數(shù)+請求token進行MD5后加密獲取

c、支持兩種方式的處理:直接拋異痴粑或者返回緩存結果

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末磷蛹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子溪烤,更是在濱河造成了極大的恐慌味咳,老刑警劉巖庇勃,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異槽驶,居然都是意外死亡责嚷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門掂铐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罕拂,“玉大人,你說我怎么就攤上這事全陨”啵” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵辱姨,是天一觀的道長柿菩。 經(jīng)常有香客問我,道長雨涛,這世上最難降的妖魔是什么枢舶? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮替久,結果婚禮上凉泄,老公的妹妹穿的比我還像新娘。我一直安慰自己蚯根,他們只是感情好后众,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稼锅,像睡著了一般吼具。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矩距,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音怖竭,去河邊找鬼锥债。 笑死,一個胖子當著我的面吹牛痊臭,可吹牛的內(nèi)容都是我干的哮肚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼广匙,長吁一口氣:“原來是場噩夢啊……” “哼允趟!你這毒婦竟也來了?” 一聲冷哼從身側響起鸦致,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤潮剪,失蹤者是張志新(化名)和其女友劉穎涣楷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抗碰,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡狮斗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了弧蝇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碳褒。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖看疗,靈堂內(nèi)的尸體忽然破棺而出沙峻,到底是詐尸還是另有隱情,我是刑警寧澤两芳,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布专酗,位于F島的核電站,受9級特大地震影響盗扇,放射性物質發(fā)生泄漏祷肯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一疗隶、第九天 我趴在偏房一處隱蔽的房頂上張望佑笋。 院中可真熱鬧,春花似錦斑鼻、人聲如沸蒋纬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜀备。三九已至,卻和暖如春荒叶,著一層夾襖步出監(jiān)牢的瞬間碾阁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工些楣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留脂凶,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓愁茁,卻偏偏與公主長得像蚕钦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鹅很,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容