新聞中心
相信大家都使用過(guò)消息MQ,他可以很好地進(jìn)行系統(tǒng)解耦,減低變成的復(fù)雜度,又可以進(jìn)行削峰,增加系統(tǒng)在高并發(fā)的穩(wěn)定性。那么使用MQ有哪些注意事項(xiàng)呢?是不是MQ就是萬(wàn)無(wú)一失呢?一條MQ消息從產(chǎn)生到消費(fèi),有沒(méi)有可能失敗?在哪些環(huán)節(jié)可能失敗,如何處理?

松原網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)公司!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、自適應(yīng)網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)公司2013年至今到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專(zhuān)注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)公司。
1.消息生產(chǎn)失敗
一般來(lái)說(shuō),從生產(chǎn)者到MQ中間件是通過(guò)網(wǎng)絡(luò)調(diào)用的,是網(wǎng)絡(luò)調(diào)用就有可能存在失敗。下面這些原因,都有可能造成MQ生產(chǎn)失敗,例如網(wǎng)絡(luò)波動(dòng),盡管生產(chǎn)者到MQ服務(wù)器之間是內(nèi)網(wǎng)調(diào)用,并不意味著網(wǎng)絡(luò)調(diào)用的成功率就是百分之百,內(nèi)網(wǎng)調(diào)用也會(huì)遇到網(wǎng)絡(luò)波動(dòng),造成調(diào)用超時(shí)或者失敗。又如調(diào)用的MQ機(jī)器瞬間Crash掉,這也是有可能造成調(diào)用失敗的。 面對(duì)生產(chǎn)者調(diào)用MQ的失敗,我們是容易比較容易處理的 , 我們只要簡(jiǎn)單地進(jìn)行重試即可,如果重試2-3次失敗,那么非常有可能是出現(xiàn)大問(wèn)題,這個(gè)時(shí)候再重試意義不大,需要進(jìn)行告警并處理。
2.MQ處理存儲(chǔ)失敗
消息到達(dá)消息中間件之后,通常是會(huì)被存儲(chǔ)起來(lái)的,只有被寫(xiě)入到磁盤(pán)中,消息才是真正地被存儲(chǔ),不會(huì)丟失。但是,大部分MQ中間件并不是收到消息就立馬寫(xiě)入磁盤(pán)的,只是由于磁盤(pán)的寫(xiě)入速度相對(duì)于內(nèi)存,現(xiàn)得慢得多得多,所以,像Kafka這樣的消息系統(tǒng),是會(huì)把消息寫(xiě)到緩沖區(qū)中,異步寫(xiě)入磁盤(pán),如果機(jī)器在中途突然斷電,是有可能會(huì)丟失消息的。為了解決這個(gè)問(wèn)題,大部分的MQ都是采用 分布式部署, 消息會(huì)在多臺(tái)機(jī)器上寫(xiě)入緩存中成功才會(huì)返回給業(yè)務(wù)方成功,由于多臺(tái)機(jī)器同時(shí)斷電的可能性較低,我們可以認(rèn)為這是比較低成本又可靠的方案。
3.消費(fèi)者處理失敗
一般的MQ都有MQ重試機(jī)制,如果處理失敗,就會(huì)嘗試重復(fù)消費(fèi)這個(gè)MQ。這個(gè)帶來(lái)的問(wèn)題就是,MQ可能已經(jīng)成功消費(fèi)了,但是在通知MQ中間件的時(shí)候失敗了,這個(gè)時(shí)候帶來(lái)的結(jié)果就是消息重復(fù)消費(fèi)。同理,在生產(chǎn)者重試的時(shí)候,也會(huì)遇到消息重復(fù)消費(fèi)的問(wèn)題。這個(gè)時(shí)候,就要求我們盡量把接口設(shè)計(jì)得有 冪等性 ,這個(gè)時(shí)候即便是重復(fù)消費(fèi),也不用擔(dān)心什么問(wèn)題了。基本上做好這三點(diǎn),我們就能夠大大地提高我們地系統(tǒng)地可用性了!
這里需要關(guān)注幾個(gè)重點(diǎn):
- 冪等不僅僅只是一次(或多次)請(qǐng)求對(duì)資源沒(méi)有副作用(比如查詢(xún)數(shù)據(jù)庫(kù)操作,沒(méi)有增刪改,因此沒(méi)有對(duì)數(shù)據(jù)庫(kù)有任何影響)。
- 冪等還包括第一次請(qǐng)求的時(shí)候?qū)Y源產(chǎn)生了副作用,但是以后的多次請(qǐng)求都不會(huì)再對(duì)資源產(chǎn)生副作用。
- 冪等關(guān)注的是以后的多次請(qǐng)求是否對(duì)資源產(chǎn)生的副作用,而不關(guān)注結(jié)果。
冪等性是系統(tǒng)服務(wù)對(duì)外一種承諾(而不是實(shí)現(xiàn)),承諾只要調(diào)用接口成功,外部多次調(diào)用對(duì)系統(tǒng)的影響是一致的。聲明為冪等的服務(wù)會(huì)認(rèn)為外部調(diào)用失敗是常態(tài),并且失敗之后必然會(huì)有重試。
什么情況下需要冪等
業(yè)務(wù)開(kāi)發(fā)中,經(jīng)常會(huì)遇到重復(fù)提交的情況,無(wú)論是由于網(wǎng)絡(luò)問(wèn)題無(wú)法收到請(qǐng)求結(jié)果而重新發(fā)起請(qǐng)求,或是前端的操作抖動(dòng)而造成重復(fù)提交情況。 在交易系統(tǒng),支付系統(tǒng)這種重復(fù)提交造成的問(wèn)題有尤其明顯,比如:
用戶(hù)在A(yíng)PP上連續(xù)點(diǎn)擊了多次提交訂單,后臺(tái)應(yīng)該只產(chǎn)生一個(gè)訂單;
向支付系統(tǒng)發(fā)起支付請(qǐng)求,由于網(wǎng)絡(luò)問(wèn)題或系統(tǒng)BUG重發(fā),支付系統(tǒng)應(yīng)該只扣一次錢(qián)。 很顯然,聲明冪等的服務(wù)認(rèn)為,外部調(diào)用者會(huì)存在多次調(diào)用的情況,為了防止外部多次調(diào)用對(duì)系統(tǒng)數(shù)據(jù)狀態(tài)的發(fā)生多次改變,將服務(wù)設(shè)計(jì)成冪等。
冪等VS防重
上面例子中遇到的問(wèn)題,只是重復(fù)提交的情況,和服務(wù)冪等的初衷是不同的。重復(fù)提交是在第一次請(qǐng)求已經(jīng)成功的情況下,人為的進(jìn)行多次操作,導(dǎo)致不滿(mǎn)足冪等要求的服務(wù)多次改變狀態(tài)。 而冪等更多使用的情況是第一次請(qǐng)求不知道結(jié)果(比如超時(shí))或者失敗的異常情況下,發(fā)起多次請(qǐng)求,目的是多次確認(rèn)第一次請(qǐng)求成功,卻不會(huì)因多次請(qǐng)求而出現(xiàn)多次的狀態(tài)變化。
什么情況下需要保證冪等性
以SQL為例,有下面三種場(chǎng)景,只有第三種場(chǎng)景需要開(kāi)發(fā)人員使用其他策略保證冪等性:
- SELECT col1 FROM tab1 WHER col2=2 ,無(wú)論執(zhí)行多少次都不會(huì)改變狀態(tài),是天然的冪等。
- UPDATE tab1 SET col1=1 WHERE col2=2 ,無(wú)論執(zhí)行 成功 多少次 狀態(tài) 都是一致的,因此也是冪等操作。
- UPDATE tab1 SET col1=col1+1 WHERE col2=2 ,每次執(zhí)行的結(jié)果都會(huì)發(fā)生變化,這種不是冪等的。
為什么要設(shè)計(jì)冪等性的服務(wù)
冪等可以使得客戶(hù)端邏輯處理變得簡(jiǎn)單,但是卻以服務(wù)邏輯變得復(fù)雜為代價(jià)。 滿(mǎn)足冪等服務(wù)的需要在邏輯中至少包含兩點(diǎn):
- 首先去查詢(xún)上一次的執(zhí)行狀態(tài),如果沒(méi)有則認(rèn)為是第一次請(qǐng)求
- 在服務(wù)改變狀態(tài)的業(yè)務(wù)邏輯前,保證防重復(fù)提交的邏輯
冪等的不足
冪等是為了簡(jiǎn)化客戶(hù)端邏輯處理,卻增加了服務(wù)提供者的邏輯和成本,是否有必要,需要根據(jù)具體場(chǎng)景具體分析, 因此除了業(yè)務(wù)上的特殊要求外,盡量不提供冪等的接口。
增加了額外控制冪等的業(yè)務(wù)邏輯,復(fù)雜化了業(yè)務(wù)功能;
把并行執(zhí)行的功能改為串行執(zhí)行,降低了執(zhí)行效率。
保證冪等策略
冪等需要通過(guò) 唯一的業(yè)務(wù)單號(hào) 來(lái)保證。也就是說(shuō)相同的業(yè)務(wù)單號(hào),認(rèn)為是同一筆業(yè)務(wù)。使用這個(gè)唯一的業(yè)務(wù)單號(hào)來(lái)確保,后面多次的相同的業(yè)務(wù)單號(hào)的處理邏輯和執(zhí)行效果是一致的。 下面以支付為例,在不考慮并發(fā)的情況下,實(shí)現(xiàn)冪等很簡(jiǎn)單:
①先查詢(xún)一下訂單是否已經(jīng)支付過(guò),
②如果已經(jīng)支付過(guò),則返回支付成功;如果沒(méi)有支付,進(jìn)行支付流程,修改訂單狀態(tài)為‘已支付’。
防重復(fù)提交策略
上述的保證冪等方案是分成兩步的,第②步依賴(lài)第①步的查詢(xún)結(jié)果,無(wú)法保證原子性的。 在高并發(fā)下就會(huì)出現(xiàn)下面的情況: 第二次請(qǐng)求在第一次請(qǐng)求第②步訂單狀態(tài)還沒(méi)有修改為‘已支付狀態(tài)’的情況下到來(lái)。 既然得出了這個(gè)結(jié)論,余下的問(wèn)題也就變得簡(jiǎn)單:把查詢(xún)和變更狀態(tài)操作加鎖,將并行操作改為串行操作。
樂(lè)觀(guān)鎖
如果只是更新 已有 的數(shù)據(jù),沒(méi)有必要對(duì)業(yè)務(wù)進(jìn)行加鎖,設(shè)計(jì)表結(jié)構(gòu)時(shí)使用樂(lè)觀(guān)鎖,一般通過(guò)version來(lái)做樂(lè)觀(guān)鎖,這樣既能保證執(zhí)行效率,又能保證冪等。例如: UPDATE tab1 SET col1=1,version=version+1 WHERE version=#version# 不過(guò), 樂(lè)觀(guān)鎖存在失效的情況,就是常說(shuō)的ABA問(wèn)題,不過(guò)如果version版本一直是自增的就不會(huì)出現(xiàn)ABA的情況。
防重表
使用訂單號(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)求完成(成功或失敗)。 可以看出防重表作用是加鎖的功能。
分布式鎖
這里使用的防重表可以使用分布式鎖代替,比如Redis。訂單發(fā)起支付請(qǐng)求,支付系統(tǒng)會(huì)去Redis緩存中查詢(xún)是否存在該訂單號(hào)的Key,如果不存在,則向Redis增加Key為訂單號(hào)。查詢(xún)訂單支付已經(jīng)支付,如果沒(méi)有則進(jìn)行支付,支付完成后刪除該訂單號(hào)的Key。通過(guò)Redis做到了分布式鎖,只有這次訂單訂單支付請(qǐng)求完成,下次請(qǐng)求才能進(jìn)來(lái)。 相比去重表,將放并發(fā)做到了緩存中,較為高效。思路相同,同一時(shí)間只能完成一次支付請(qǐng)求。
token令牌
這種方式分成兩個(gè)階段:申請(qǐng)token階段和支付階段。 第一階段,在進(jìn)入到提交訂單頁(yè)面之前,需要訂單系統(tǒng)根據(jù)用戶(hù)信息向支付系統(tǒng)發(fā)起一次申請(qǐng)token的請(qǐng)求,支付系統(tǒng)將token保存到Redis緩存中,為第二階段支付使用。 第二階段,訂單系統(tǒng)拿著申請(qǐng)到的token發(fā)起支付請(qǐng)求,支付系統(tǒng)會(huì)檢查Redis中是否存在該token,如果存在,表示第一次發(fā)起支付請(qǐng)求,刪除緩存中token后開(kāi)始支付邏輯處理;如果緩存中不存在,表示非法請(qǐng)求。 實(shí)際上這里的token是一個(gè)信物,支付系統(tǒng)根據(jù)token確認(rèn),你是你媽的孩子。 不足是需要系統(tǒng)間交互兩次,流程較上述方法復(fù)雜。
支付緩沖區(qū)
把訂單的支付請(qǐng)求都快速地接下來(lái),一個(gè)快速接單的緩沖管道。后續(xù)使用異步任務(wù)處理管道中的數(shù)據(jù),過(guò)濾掉重復(fù)的待支付訂單。 優(yōu)點(diǎn)是同步轉(zhuǎn)異步,高吞吐。不足是不能及時(shí)地返回支付結(jié)果,需要后續(xù)監(jiān)聽(tīng)支付結(jié)果的異步返回。
本文名稱(chēng):消息隊(duì)列失敗經(jīng)驗(yàn)總結(jié)(冪等性概念以及影響)
鏈接分享:http://www.dlmjj.cn/article/djddgid.html


咨詢(xún)
建站咨詢(xún)
