實(shí)現(xiàn)業(yè)務(wù)冪等性的常用方案!
重復(fù)請(qǐng)求
生產(chǎn)環(huán)境經(jīng)常出現(xiàn)重復(fù)的數(shù)據(jù),而這個(gè)絕大部分原因是發(fā)生了重復(fù)的請(qǐng)求。
重復(fù)請(qǐng)求是指同一個(gè)請(qǐng)求因?yàn)槟承┰虮欢啻翁峤弧?/p>
導(dǎo)致這個(gè)情況會(huì)有幾種場(chǎng)景:
微服務(wù)場(chǎng)景:
- 在微服務(wù)架構(gòu)下,接口超時(shí),微服務(wù)框架會(huì)進(jìn)行重試。
用戶(hù)交互的時(shí)候多次點(diǎn)擊,如:快速點(diǎn)擊按鈕多次。
MQ消息中間件:消息重復(fù)消費(fèi)。
第三方平臺(tái)接口:因?yàn)楫惓R矔?huì)導(dǎo)致多次異步回調(diào)。
冪等性定義
如上,針對(duì)重復(fù)請(qǐng)求,我們?cè)谠O(shè)計(jì)某些接口時(shí),要考慮如何保證接口冪等。
那什么是接口冪等呢?
定義:多次調(diào)用對(duì)系統(tǒng)的產(chǎn)生的影響是一樣的,即對(duì)資源的作用是一樣的,但是返回值允許不同。
- 并且不會(huì)因?yàn)槎啻吸c(diǎn)擊而產(chǎn)生了副作用。
比如支付場(chǎng)景:
用戶(hù)購(gòu)買(mǎi)了商品支付扣款成功,但是返回結(jié)果的時(shí)候網(wǎng)絡(luò)異常,此時(shí)錢(qián)已經(jīng)扣了。
用戶(hù)再次點(diǎn)擊按鈕,此時(shí)會(huì)進(jìn)行第二次扣款,返回結(jié)果成功。
- 如果用戶(hù)查詢(xún)余額返發(fā)現(xiàn)多扣錢(qián)了,流水記錄也變成了兩條,這就沒(méi)有保證接口的冪等性。
冪等場(chǎng)景
查詢(xún)
- 不會(huì)對(duì)數(shù)據(jù)產(chǎn)生任何變化,具備冪等性。
新增
- 數(shù)據(jù)都會(huì)新增多條,不具備冪等性。
修改
- 直接賦值更新,具備冪等性。
- 計(jì)算賦值更新,不具備冪等性。
刪除
- 多次操作,結(jié)果一樣,具備冪等性。
總結(jié):新增沒(méi)有唯一主鍵約束的數(shù)據(jù),和計(jì)算賦值更新操作都不具備冪等性。
直接賦值:
update user set num = 20 where userid=123;
計(jì)算賦值:
update user set num = num + 20 where userid=123;
冪等性方案
悲觀鎖
id字段一定是主鍵或者唯一索引,不然可能造成鎖表的結(jié)果。
- 數(shù)據(jù)鎖定時(shí)間可能會(huì)很長(zhǎng),需要根據(jù)實(shí)際情況選用。
select * from user where id = 1 for update;
樂(lè)觀鎖
加上了版本號(hào)后,計(jì)算賦值更新場(chǎng)景,具備了冪等性。
缺點(diǎn):
- 在操作業(yè)務(wù)前,需要先查詢(xún)出當(dāng)前的version版本。
update user set num= num + 20, version = version + 1 where userid=123 and version=1;
唯一約束
利用數(shù)據(jù)庫(kù)的主鍵唯一約束的特性,解決在insert場(chǎng)景時(shí)冪等問(wèn)題。
去重表
把唯一主鍵插入去重表,再進(jìn)行業(yè)務(wù)操作,且他們?cè)谕粋€(gè)事務(wù)中。
- 這個(gè)保證了重復(fù)請(qǐng)求時(shí),因?yàn)槿ブ乇碛?strong>唯一約束,導(dǎo)致請(qǐng)求失敗,避免了冪等問(wèn)題。
去重表和業(yè)務(wù)表應(yīng)該在同一庫(kù)中,這樣就保證了在同一個(gè)事務(wù),即使業(yè)務(wù)操作失敗了,也會(huì)把去重表的數(shù)據(jù)回滾。
- 保證了數(shù)據(jù)一致性。
去重表是跟業(yè)務(wù)無(wú)關(guān)的,很多業(yè)務(wù)可以共用同一個(gè)去重表。
- 需要規(guī)劃好唯一主鍵。
圖片
分布式鎖
如果多個(gè)機(jī)器可能在同一時(shí)間同時(shí)處理相同的數(shù)據(jù):
- 比如多臺(tái)機(jī)器定時(shí)任務(wù)都拿到了相同數(shù)據(jù)處理。
就可以加分布式鎖,鎖定此數(shù)據(jù),處理完成后釋放鎖。
- 獲取到鎖的必須先判斷這個(gè)數(shù)據(jù)是否被處理過(guò)。
Token機(jī)制
圖片
?
服務(wù)端提供了發(fā)送token的接口。
在執(zhí)行業(yè)務(wù)前,先去獲取token,服務(wù)器會(huì)把token保存到redis中。
- 然后調(diào)用業(yè)務(wù)接口請(qǐng)求時(shí),把token攜帶過(guò)去。
服務(wù)器判斷token是否存在redis中,存在表示第一次請(qǐng)求。
- 這時(shí)把redis中的token刪除,繼續(xù)執(zhí)行業(yè)務(wù)。
如果判斷token不存在redis中,就表示是重復(fù)操作,直接返回重復(fù)標(biāo)記給client。
- 這樣就保證了業(yè)務(wù)代碼,不被重復(fù)執(zhí)行。
缺點(diǎn)
業(yè)務(wù)請(qǐng)求每次請(qǐng)求,都會(huì)有額外的請(qǐng)求:
- 一次獲取token請(qǐng)求、判斷token是否存在。
真實(shí)的生產(chǎn)環(huán)境中,1萬(wàn)請(qǐng)求也許只會(huì)存在10個(gè)左右的請(qǐng)求會(huì)發(fā)生重試。
- 為了這10個(gè)請(qǐng)求,需要讓9990個(gè)請(qǐng)求都發(fā)生了額外的請(qǐng)求。
邏輯實(shí)現(xiàn):
可以通過(guò)自定義注解將進(jìn)行改造。
- 在需要保證冪等的方法上,添加自定義注解即。
圖片
總結(jié)
冪等性是系統(tǒng)服務(wù)對(duì)外一種承諾,特別業(yè)務(wù)中涉及的錢(qián)的部分,一定要慎重再慎重。
雖然前端做限制會(huì)更容易點(diǎn),但前后端都需要做努力。