高并發(fā)下的接口冪等性如何實(shí)現(xiàn)

轉(zhuǎn)自https://mp.weixin.qq.com/s/odRypb6YqF3xuRn-4YAwlg
https://blog.csdn.net/wanglei303707/article/details/88298211

冪等就是一個(gè)操作稠曼,不論執(zhí)行多少次虚循,產(chǎn)生的效果和返回的結(jié)果都是一樣的缸棵。

1圈澈、防止頁(yè)面重復(fù)提交——token

1.1 場(chǎng)景

頁(yè)面的數(shù)據(jù)只能被點(diǎn)擊提交一次

1.2 發(fā)生原因

由于重復(fù)點(diǎn)擊或者網(wǎng)絡(luò)重發(fā)开仰,或者nginx重發(fā)等情況會(huì)導(dǎo)致數(shù)據(jù)被重復(fù)提交

1.3場(chǎng)景:

場(chǎng)景一:在網(wǎng)絡(luò)延遲的情況下讓用戶(hù)有時(shí)間點(diǎn)擊多次submit按鈕導(dǎo)致表單重復(fù)提交
場(chǎng)景二:表單提交后用戶(hù)點(diǎn)擊【刷新】按鈕導(dǎo)致表單重復(fù)提交
場(chǎng)景三:用戶(hù)提交表單后苗傅,點(diǎn)擊瀏覽器的【后退】按鈕回退到表單頁(yè)面后進(jìn)行再次提交

1.4 解決辦法

集群環(huán)境:采用token加redis(redis單線(xiàn)程的熬甫,處理需要排隊(duì))
單JVM環(huán)境:采用token加redis或token加jvm內(nèi)存

1.5 處理流程:

1.5.1 數(shù)據(jù)提交前要向服務(wù)的申請(qǐng)token唉锌,token放到redis或jvm內(nèi)存诗舰,token有效時(shí)間警儒;
1.5.2 前端提交后,后臺(tái)校驗(yàn)token,同時(shí)刪除token蜀铲,執(zhí)行業(yè)務(wù)生成新的token返回
token特點(diǎn):要申請(qǐng)边琉,一次有效性,可以限流

因?yàn)閞edis單線(xiàn)程的原因记劝,當(dāng)多次提交時(shí)变姨,redis需要排隊(duì)處理,所以只有第一次提交可以刪除token成功厌丑,當(dāng)刪除成功代表token校驗(yàn)通過(guò)定欧,刪除失敗,說(shuō)明是重復(fù)提交怒竿。

image.png

2砍鸠、防止新增臟數(shù)據(jù)(重復(fù)數(shù)據(jù))——唯一索引

查詢(xún)操作和刪除操作天然就是冪等的:

  • 查詢(xún)操作:查詢(xún)一次和查詢(xún)多次,在數(shù)據(jù)不變的情況下耕驰,查詢(xún)結(jié)果是一樣的爷辱。select是天然的冪等操作;

  • 刪除操作:刪除操作也是冪等的朦肘,刪除一次和多次刪除都是把數(shù)據(jù)刪除饭弓。(注意可能返回結(jié)果不一樣,刪除的數(shù)據(jù)不存在厚骗,返回0示启,刪除的數(shù)據(jù)多條兢哭,返回結(jié)果多個(gè))

  • 新增操作:有些服務(wù)是有重試機(jī)制的(ribbon)领舰,在這種情況下,就可能會(huì)出現(xiàn)新增兩條一樣的數(shù)據(jù)迟螺,這時(shí)候服務(wù)要支持冪等操作冲秽,否則會(huì)出問(wèn)題,這時(shí)候可以通過(guò)數(shù)據(jù)庫(kù)唯一索引來(lái)防止新增臟數(shù)據(jù)矩父。
    比如:支付寶的資金賬戶(hù)锉桑,支付寶也有用戶(hù)賬戶(hù),每個(gè)用戶(hù)只能有一個(gè)資金賬戶(hù)窍株,怎么防止給用戶(hù)創(chuàng)建資金賬戶(hù)多個(gè)民轴,那么給資金賬戶(hù)表中的用戶(hù)ID加唯一索引,所以一個(gè)用戶(hù)新增成功一個(gè)資金賬戶(hù)記錄

2.1 唯一索引或唯一組合索引來(lái)防止新增數(shù)據(jù)存在臟數(shù)據(jù)

當(dāng)表存在唯一索引球订,并發(fā)時(shí)新增報(bào)錯(cuò)時(shí)后裸,再查詢(xún)一次就可以了,數(shù)據(jù)應(yīng)該已經(jīng)存在了冒滩,返回結(jié)果即可

2.2 防重表

使用訂單號(hào)orderNo做為去重表的唯一索引微驶,每次請(qǐng)求都根據(jù)訂單號(hào)向去重表中插入一條數(shù)據(jù)。第一次請(qǐng)求查詢(xún)訂單支付狀態(tài),當(dāng)然訂單沒(méi)有支付因苹,進(jìn)行支付操作苟耻,無(wú)論成功與否,執(zhí)行完后更新訂單狀態(tài)為成功或失敗扶檐,刪除去重表中的數(shù)據(jù)凶杖。后續(xù)的訂單因?yàn)楸碇形ㄒ凰饕迦胧。瑒t返回操作失敗款筑,直到第一次的請(qǐng)求完成(成功或失敼倏ā)〈茁玻可以看出防重表作用是加鎖的功能寻咒。

image.png

防重表可以在一些主業(yè)務(wù)表不方便加唯一索引或者唯一組合索引時(shí)其作用,通過(guò)單獨(dú)新建一張防重表來(lái)解決問(wèn)題

3颈嚼、對(duì)外提供接口的api如何保證冪等

如銀聯(lián)提供的付款接口:需要接入商戶(hù)提交付款請(qǐng)求時(shí)附帶:source來(lái)源毛秘,seq序列號(hào),source+seq在數(shù)據(jù)庫(kù)里面做唯一索引阻课,防止多次付款叫挟,(并發(fā)時(shí),只能處理一個(gè)請(qǐng)求)

重點(diǎn): 對(duì)外提供接口為了支持冪等調(diào)用限煞,接口有兩個(gè)字段必須傳抹恳,一個(gè)是來(lái)源source,一個(gè)是來(lái)源方序列號(hào)seq署驻,這個(gè)兩個(gè)字段在提供方系統(tǒng)里面做聯(lián)合唯一索引

這樣當(dāng)?shù)谌秸{(diào)用時(shí)奋献,先在本方系統(tǒng)里面查詢(xún)一下,是否已經(jīng)處理過(guò)旺上,返回相應(yīng)處理結(jié)果瓶蚂;沒(méi)有處理過(guò),進(jìn)行相應(yīng)處理宣吱,返回結(jié)果窃这。

注意,為了冪等友好征候,一定要先查詢(xún)一下杭攻,是否處理過(guò)該筆業(yè)務(wù),不查詢(xún)直接插入業(yè)務(wù)系統(tǒng)疤坝,會(huì)報(bào)錯(cuò)兆解,但實(shí)際已經(jīng)處理了。

這里使用的還是數(shù)據(jù)庫(kù)唯一索引(聯(lián)合唯一索引)卒煞。

對(duì)于一些有時(shí)效性的業(yè)務(wù)處理接口來(lái)說(shuō)痪宰,事實(shí)上還有一種方法,就是使用redis。

  1. 當(dāng)?shù)谌秸{(diào)用時(shí)衣撬,先判斷是否過(guò)期乖订,如果過(guò)期了,就不處理具练,如果沒(méi)有過(guò)期乍构,則下一步;
  2. 判斷redis中是否存在source_seq key扛点,如果存在哥遮,說(shuō)明已經(jīng)處理過(guò)了,是重復(fù)調(diào)用陵究,直接返回眠饮;如果不存在,則set source_seq key 铜邮,注意這里的存在則返回仪召,不存在則set key,是需要保持原子操作的松蒜,可以通過(guò)lua腳本來(lái)提交扔茅;
  3. key有效期和業(yè)務(wù)有關(guān)系, 比如說(shuō)我支付消息有效期只有1h秸苗,那么key 有效期也是1h召娜;

4、同一時(shí)間只能完成一次請(qǐng)求——樂(lè)觀鎖惊楼、分布式鎖

在分布式環(huán)境下玖瘸,因?yàn)榫W(wǎng)絡(luò)、重試等原因?qū)е乱粋€(gè)長(zhǎng)流程請(qǐng)求經(jīng)過(guò)不同的實(shí)例時(shí)胁后,會(huì)發(fā)生重復(fù)操作店读,為了防止這種情況,可以采用樂(lè)觀鎖攀芯,或者分布式鎖來(lái)解決。

image.png
image.png

使用樂(lè)觀鎖文虏,或者分布式鎖侣诺,其實(shí)就是在mysql中,同一時(shí)間只有一次請(qǐng)求氧秘。
在有些場(chǎng)景下年鸳,使用version樂(lè)觀鎖還是麻煩,其實(shí)可以使用主鍵id丸相,或者唯一索引來(lái)鎖定mysql數(shù)據(jù)搔确,然后再操作,比如狀態(tài)變更,如下

update tableA set status = 5 where id=4 and status=4;

就算同一時(shí)間有重復(fù)操作膳算,但是因?yàn)閙ysql行鎖的特性座硕,同一時(shí)間只有一個(gè)操作能鎖定id為4的這行數(shù)據(jù),狀態(tài)變更成功以后(返回值為1)涕蜂,后面的操作已經(jīng)不滿(mǎn)足status=4的條件了华匾,利用這個(gè)特性,不止能防重復(fù)机隙,對(duì)于一些并發(fā)開(kāi)獎(jiǎng)場(chǎng)景蜘拉,可以直接替代樂(lè)觀鎖的使用。

5有鹿、總結(jié)

查詢(xún)操作:select旭旭,查詢(xún)一次和查詢(xún)多次,在數(shù)據(jù)不變的情況下葱跋,查詢(xún)結(jié)果是一樣的您机。select是天然的冪等操作;
刪除操作:delete年局,刪除操作也是冪等的际看,刪除一次和多次刪除都是把數(shù)據(jù)刪除。(注意可能返回結(jié)果不一樣矢否,刪除的數(shù)據(jù)不存在仲闽,返回0,刪除的數(shù)據(jù)多條僵朗,返回結(jié)果多個(gè))赖欣;
新增操作:insert,業(yè)務(wù)邏輯上验庙,先select再insert示血;數(shù)據(jù)庫(kù)上盡量通過(guò)唯一索引或唯一組合索引來(lái)防止新增臟數(shù)據(jù)(重復(fù)數(shù)據(jù))喇肋;如果沒(méi)有唯一索引,則再考慮分布式鎖。
更新操作:update拢切,對(duì)于狀態(tài)操作,where條件中魏滚,一定要加上原本的狀態(tài)值扫倡,即明確是由舊狀態(tài)變更為新?tīng)顟B(tài),這個(gè)過(guò)程是不可逆的藤巢。

如果上面的一般方法還是不能防重搞莺,則可以使用redis分布式鎖。

使用token+redis的好處是掂咒,一個(gè)token只能使用一次才沧,token使用或者過(guò)期后迈喉,就只能再次申請(qǐng)token,是嚴(yán)格防止重復(fù)的温圆。但是會(huì)比較麻煩挨摸,一個(gè)請(qǐng)求,需要兩次操作捌木,一次獲取token油坝,一次執(zhí)行業(yè)務(wù)。

其實(shí)可以簡(jiǎn)單的只用redis分布式鎖來(lái)防止重復(fù)刨裆,把獲取鎖和釋放鎖的邏輯寫(xiě)在過(guò)濾器澈圈,或者攔截器中,只有獲取到鎖帆啃,才能執(zhí)行業(yè)務(wù)邏輯瞬女。—— 對(duì)那些需要加防重的接口url努潘,可以放在配置文件中诽偷,而不要放在redis中,因?yàn)榻^對(duì)部分接口url是不需要防重的疯坤,如果放在redis中报慕,每個(gè)請(qǐng)求都會(huì)去redis中獲取url配置,太浪費(fèi)redis了压怠,如果趕上公司搞活動(dòng)眠冈,很容易就報(bào)警了。

key的設(shè)置:可以先把請(qǐng)求參數(shù)中為空的去掉菌瘫,然后拼接為String蜗顽,并對(duì)其MD5加密,再加上userId雨让,最后拼接成key雇盖。

至于redis分布式的實(shí)現(xiàn),可以見(jiàn)redis系列文章栖忠。http://www.reibang.com/nb/39445385

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末崔挖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子娃闲,更是在濱河造成了極大的恐慌虚汛,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皇帮,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蛋辈,警方通過(guò)查閱死者的電腦和手機(jī)属拾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)将谊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人渐白,你說(shuō)我怎么就攤上這事尊浓。” “怎么了纯衍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵栋齿,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我襟诸,道長(zhǎng)瓦堵,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任歌亲,我火速辦了婚禮菇用,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘陷揪。我一直安慰自己惋鸥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布悍缠。 她就那樣靜靜地躺著卦绣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪飞蚓。 梳的紋絲不亂的頭發(fā)上滤港,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音玷坠,去河邊找鬼蜗搔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛八堡,可吹牛的內(nèi)容都是我干的樟凄。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼兄渺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼缝龄!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起挂谍,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤叔壤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后口叙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體炼绘,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年妄田,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俺亮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驮捍。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖脚曾,靈堂內(nèi)的尸體忽然破棺而出东且,到底是詐尸還是另有隱情,我是刑警寧澤本讥,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布珊泳,位于F島的核電站,受9級(jí)特大地震影響拷沸,放射性物質(zhì)發(fā)生泄漏色查。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一堵漱、第九天 我趴在偏房一處隱蔽的房頂上張望综慎。 院中可真熱鬧,春花似錦勤庐、人聲如沸示惊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)米罚。三九已至,卻和暖如春丈探,著一層夾襖步出監(jiān)牢的瞬間录择,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工碗降, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隘竭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓讼渊,卻偏偏與公主長(zhǎng)得像动看,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子爪幻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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