日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
大廠都怎么防止重復(fù)下單?

1.問(wèn)題背景

最簡(jiǎn)單的:DB事務(wù)。如創(chuàng)建訂單時(shí),同時(shí)往訂單表、訂單商品表插數(shù)據(jù),這些Insert須在同一事務(wù)執(zhí)行。

公司主營(yíng)業(yè)務(wù):做網(wǎng)站、網(wǎng)站建設(shè)、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。成都創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。成都創(chuàng)新互聯(lián)公司推出冀州免費(fèi)做網(wǎng)站回饋大家。

Order服務(wù)調(diào)用Pay服務(wù),剛好網(wǎng)絡(luò)超時(shí),然后Order服務(wù)開始重試機(jī)制,于是Pay服務(wù)對(duì)同一支付請(qǐng)求,就接收到了兩次,而且因?yàn)檩喸冐?fù)載均衡算法,落在了不同業(yè)務(wù)節(jié)點(diǎn)!所以一個(gè)分布式系統(tǒng)接口,須保證冪等性。

2.如何避免重復(fù)下單?

前端頁(yè)面也可直接防止用戶重復(fù)提交表單,但網(wǎng)絡(luò)錯(cuò)誤會(huì)導(dǎo)致重傳,很多RPC框架、網(wǎng)關(guān)都有自動(dòng)重試機(jī)制,所以重復(fù)請(qǐng)求在前端側(cè)無(wú)法完全避免!問(wèn)題最后還是如何保證服務(wù)接口的冪等性。

2.1 如何判斷請(qǐng)求是重復(fù)的?

  • 插入訂單前,先查一下訂單表,有無(wú)重復(fù)訂單? 不優(yōu):難以用SQL條件定義到底什么是“重復(fù)訂單”
  • 訂單的用戶、商品、價(jià)格一樣就是重復(fù)訂單? 萬(wàn)一這用戶就是連續(xù)下了倆一模一樣訂單呢?

所以保證冪等性要做到:

2.1.1 每個(gè)請(qǐng)求須有唯一標(biāo)識(shí)

比如訂單支付請(qǐng)求,得包含訂單id,一個(gè)訂單id最多只能成功支付一次。

2.1.2 每次處理完請(qǐng)求后,須有記錄標(biāo)識(shí)該請(qǐng)求已被處理

在MySQL中記錄一個(gè)狀態(tài)字段。如支付之前記錄一條這個(gè)訂單的支付流水。

2.1.3 每次接收請(qǐng)求時(shí),判斷之前是否處理過(guò)

若有一個(gè)訂單已支付,就肯定已有一條支付流水。若重復(fù)發(fā)送這個(gè)請(qǐng)求,則此時(shí)先插入支付流水,發(fā)現(xiàn)orderId已存在,唯一約束生效,報(bào)錯(cuò)重復(fù)Key。就不會(huì)再重復(fù)扣款。

在往DB插記錄時(shí),一般不提供主鍵,而由DB在插入時(shí)自動(dòng)生成。這樣重復(fù)的請(qǐng)求就會(huì)導(dǎo)致插入重復(fù)的數(shù)據(jù)。MySQL的主鍵自帶唯一性約束,若在一條INSERT語(yǔ)句提供主鍵,且該主鍵值在表中已存在,則該條INSERT會(huì)執(zhí)行失敗。因此可利用DB的“主鍵唯一約束”,在插數(shù)據(jù)時(shí)帶上主鍵,以此實(shí)現(xiàn)創(chuàng)建訂單接口的冪等性。

給Order服務(wù)添加一個(gè)“orderId生成”的接口,無(wú)參,返回值就是一個(gè)【全局唯一】訂單號(hào)。在用戶進(jìn)入創(chuàng)建訂單頁(yè)面時(shí),前端頁(yè)面先調(diào)用該orderId生成接口得到一個(gè)訂單號(hào),在用戶提交訂單時(shí),在創(chuàng)建訂單的請(qǐng)求中攜帶該訂單號(hào)。

該訂單號(hào)其實(shí)就是訂單表的主鍵,于是,重復(fù)請(qǐng)求中帶的都是同一訂單號(hào)。訂單服務(wù)在訂單表中插入數(shù)據(jù)的時(shí)候,執(zhí)行的這些重復(fù)INSERT語(yǔ)句中的主鍵,也都是同一個(gè)訂單號(hào)。而DB唯一約束保證,只有一次INSERT執(zhí)行成功。

實(shí)際要結(jié)合業(yè)務(wù),如使用Redis,用orderId作為唯一K。只有成功插入這個(gè)支付流水,才可執(zhí)行扣款。

要求是支付一個(gè)訂單,須插入一條支付流水,order_id建立一個(gè)唯一鍵。你在支付一個(gè)訂單前,先插入一條支付流水,order_id就已經(jīng)傳過(guò)去了。就能寫一個(gè)標(biāo)識(shí)到Redis中,set order_id payed,當(dāng)重復(fù)請(qǐng)求過(guò)來(lái)時(shí),先查Redis的order_id對(duì)應(yīng)的value,若為payed說(shuō)明已支付,就別再重復(fù)支付!

然后再重復(fù)支付訂單時(shí),寫嘗試插入一條支付流水,DB會(huì)報(bào)唯一鍵沖突,整個(gè)事務(wù)回滾。保存一個(gè)是否處理過(guò)的標(biāo)識(shí)也可以,服務(wù)的不同實(shí)例可以一起操作Redis。

若因重復(fù)訂單導(dǎo)致插入 t_order 失敗,則Order服務(wù)不要把該錯(cuò)誤返給前端頁(yè)面。否則,就可能出現(xiàn)用戶點(diǎn)擊創(chuàng)建訂單按鈕后,頁(yè)面提示創(chuàng)建訂單失敗,而實(shí)際上訂單創(chuàng)建成功了。

正確做法:這種case,訂單服務(wù)直接返回訂單創(chuàng)建成功。

3.解決ABA

3.1 什么是ABA

如訂單支付后,seller要發(fā)貨,發(fā)貨完成后要填個(gè)快遞單號(hào)。假設(shè)seller填個(gè)666,剛填完,發(fā)現(xiàn)填錯(cuò)了,趕緊再修改成888。對(duì)訂單服務(wù),這就是2個(gè)更新訂單的請(qǐng)求。系統(tǒng)異常時(shí)666請(qǐng)求到了,單號(hào)更成666,接著888請(qǐng)求到了,單號(hào)又更新成888,但是666更新成功的響應(yīng)丟了,調(diào)用方?jīng)]收到成功響應(yīng),自動(dòng)重試,再次發(fā)起666請(qǐng)求,單號(hào)又被更新成666了,這數(shù)據(jù)顯然就錯(cuò)了!

3.2 解決方案

訂單主表增加version列。每次查詢訂單時(shí),版本號(hào)要隨著訂單數(shù)據(jù)返回給頁(yè)面。頁(yè)面在更新數(shù)據(jù)的請(qǐng)求中,把這個(gè)版本號(hào)作為更新請(qǐng)求的參數(shù),帶回給訂單更新接口。

訂單服務(wù)在更新數(shù)據(jù)的時(shí)候,需要比較訂單的版本號(hào)是否和消息中的一致:

  • 不一致拒絕更新數(shù)據(jù)
  • 一致還需再更新數(shù)據(jù)的同時(shí),將version+1。“比較版本號(hào)、更新數(shù)據(jù)和版本號(hào)+1”的過(guò)程須在同一事務(wù)執(zhí)行
UPDATE orders set tracking_number = 666,
version = version + 1
WHERE version = 8;

在這條SQL的WHERE條件中,version值需要頁(yè)面在更新的時(shí)候通過(guò)請(qǐng)求傳進(jìn)來(lái)。

通過(guò)該版本號(hào),就能保證,從我打開這條訂單記錄開始,一直到我更新這條訂單記錄成功,期間沒(méi)有其他人修改過(guò)該訂單數(shù)據(jù)。若有,則DB中的version就會(huì)改變,那我的更新操作就會(huì)執(zhí)行失敗。我就只能重新查詢新版本的訂單數(shù)據(jù),再嘗試更新。

有了這個(gè)版本號(hào),前文的ABA即有兩個(gè)case:

  •  把運(yùn)單號(hào)更新為666成功,更新為888的請(qǐng)求帶著舊版本號(hào),就更新失敗,頁(yè)面提示用戶更新888失敗
  • 666更新成功后,888帶著新版本號(hào),888更新成功。這時(shí)即使重試的666請(qǐng)求再來(lái),因?yàn)樗蜕弦粭l666請(qǐng)求帶相同版本號(hào),上一條請(qǐng)求更新成功后,這個(gè)版本號(hào)已經(jīng)變了,所以重試請(qǐng)求的更新必然失敗

無(wú)論哪種情況,DB中的數(shù)據(jù)與頁(yè)面上給用戶的反饋都是一致的。這就實(shí)現(xiàn)了冪等更新且避免ABA。

4.總結(jié)

  • 創(chuàng)建訂單服務(wù),可通過(guò)預(yù)生成訂單號(hào),然后利用DB的訂單號(hào)唯一約束,避免重復(fù)寫入訂單,實(shí)現(xiàn)創(chuàng)建訂單服務(wù)的冪等性
  • 更新訂單服務(wù),通過(guò)一個(gè)版本號(hào)機(jī)制,每次更新數(shù)據(jù)前校驗(yàn)版本號(hào),更新數(shù)據(jù)同時(shí)自增版本號(hào),這樣的方式,來(lái)解決ABA問(wèn)題,確保更新訂單服務(wù)的冪等性

兩種冪等的實(shí)現(xiàn)方法,就可以保證,無(wú)論請(qǐng)求是不是重復(fù),訂單表中的數(shù)據(jù)都是正確的。

實(shí)現(xiàn)訂單冪等的方法,完全可以套用在其他需要實(shí)現(xiàn)冪等的服務(wù)中,只需要這個(gè)服務(wù)操作的數(shù)據(jù)保存在數(shù)據(jù)庫(kù)中,并且有一張帶有主鍵的數(shù)據(jù)表即可。


文章標(biāo)題:大廠都怎么防止重復(fù)下單?
URL地址:http://m.5511xx.com/article/ccsossj.html