消息隊列的事務(wù)消息

      網(wǎng)友投稿 1045 2025-03-31

      1 MQ事務(wù)的意義

      “發(fā)消息”過程,往往是為通知另外一個系統(tǒng)更新數(shù)據(jù),MQ的“事務(wù)”,主要解決Pro和Con的消息數(shù)據(jù)一致性問題。

      用戶在電商APP上購物時

      先把商品加到購物車

      然后幾件商品一起下單

      最后支付

      完成購物流程,就可以愉快地等待收貨

      該過程中有個需用MQ。

      訂單系統(tǒng)創(chuàng)建訂單后,發(fā)消息給購物車模塊,將已下單商品從購物車刪除。

      從購物車刪除已下單商品步驟,并非用戶下單支付這個主要流程的必需步驟,所以使用MQ異步清理購物車。

      訂單模塊創(chuàng)建訂單的過程執(zhí)行了如下操作:

      在訂單DB插一條訂單數(shù)據(jù),以創(chuàng)建訂單

      發(fā)消息給MQ,消息內(nèi)容即剛創(chuàng)建的訂單

      購物車模塊訂閱相應(yīng)Topic,接收訂單創(chuàng)建的消息,然后清理購物車,在購物車中刪除訂單中的商品。分布式下的這些步驟都有失敗可能性,若不做處理,就可能導(dǎo)致訂單數(shù)據(jù)與購物車數(shù)據(jù)不一致:

      創(chuàng)建了訂單,沒有清理購物車

      訂單沒創(chuàng)建成功,購物車里面的商品卻被清了

      因此在任意步驟都可能失敗時,要保證訂單DB和購物車DB的數(shù)據(jù)一致性。購物車系統(tǒng)收到訂單創(chuàng)建成功消息清理購物車操作,只要成功執(zhí)行購物車清理后再提交消費確認即可。

      如果失敗,由于沒有提交消費確認,MQ會自動重試。

      問題關(guān)鍵點在訂單系統(tǒng),創(chuàng)建訂單和發(fā)送消息不允許一個成功而另一個失敗。

      這就是事務(wù)問題。

      2 分布式事務(wù)

      單體關(guān)系型數(shù)據(jù)庫都完整的實現(xiàn)ACID,但對分布式系統(tǒng)

      嚴格實現(xiàn)ACID,幾乎不可能

      或?qū)崿F(xiàn)代價太大,無法接受

      分布式系統(tǒng)在保證可用性和不嚴重犧牲性能前提下,要實現(xiàn)數(shù)據(jù)一致性非常困難,所以出現(xiàn)很多“殘血版”一致性,如順序一致性、最終一致性。

      所以分布式事務(wù)更多是在分布式系統(tǒng)中事務(wù)的不完整實現(xiàn)。在不同場景有不同實現(xiàn),都是通過一些妥協(xié)解決問題。常見分布式事務(wù)實現(xiàn)有2PC、TCC和事務(wù)消息。每種實現(xiàn)都有其特定的使用場景,也有各自問題,沒有完美無缺的方案。

      3 事務(wù)消息適用場景

      消息隊列的事務(wù)消息

      主要是那些需要異步更新數(shù)據(jù),并且對數(shù)據(jù)實時性要求不高。

      比如在創(chuàng)建訂單后,如果出現(xiàn)短暫幾s,購物車商品沒被及時清空,也不是完全不可接受,只要最終購物車的數(shù)據(jù)和訂單數(shù)據(jù)保持一致。

      4 MQ實現(xiàn)分布式事務(wù)

      事務(wù)消息需要MQ提供相應(yīng)功能才能實現(xiàn),Kafka和RocketMQ都支持事務(wù)功能:

      注意:第二步發(fā)送半消息第三步創(chuàng)建訂單,這2個順序反一下是等價的,即先創(chuàng)建訂單在發(fā)送半消息。

      半消息并非消息內(nèi)容不完整,包含的就是完整的消息內(nèi)容,和普通消息的唯一區(qū)別:

      在事務(wù)提交前,對消費者,該消息不可見。

      半消息發(fā)成功后,訂單系統(tǒng)即可執(zhí)行本地事務(wù)(創(chuàng)建訂單這個數(shù)據(jù)庫事務(wù)):

      在訂單庫創(chuàng)建一條訂單記錄,并提交訂單庫的數(shù)據(jù)庫事務(wù)

      然后根據(jù)本地事務(wù)執(zhí)行結(jié)果,決定提交或回滾事務(wù)消息

      訂單創(chuàng)建成功,提交事務(wù)消息,購物車系統(tǒng)即可消費到該消息,繼續(xù)后續(xù)流程

      訂單創(chuàng)建失敗,回滾事務(wù)消息,購物車系統(tǒng)不會收到該消息

      但有問題:若在第4步提交事務(wù)消息時失敗了咋辦?對此,Kafka和RocketMQ給出不同解決方案

      Kafka直接拋異常,讓用戶自行處理,我們能:

      在業(yè)務(wù)代碼反復(fù)重試提交,直到提交成功

      或刪除之前創(chuàng)建的訂單進行補償

      下面詳解 rocketmq 的方案。

      5 RocketMQ分布式事務(wù)

      RocketMQ的事務(wù)實現(xiàn)增加了事務(wù)反查機制,來解決事務(wù)消息提交失敗的問題。

      若Pro(訂單模塊)在提交或回滾事務(wù)消息時發(fā)生網(wǎng)絡(luò)異常,導(dǎo)致Broker沒收到提交或回滾請求,Broker會定期去Pro反查該事務(wù)對應(yīng)的本地事務(wù)的狀態(tài),然后根據(jù)反查結(jié)果決定提交或回滾該事務(wù)。

      要支持事務(wù)反查,業(yè)務(wù)代碼需實現(xiàn)一個反查本地事務(wù)狀態(tài)的接口,告知RocketMQ本地事務(wù)是成功還是失敗。

      若反查的服務(wù)器數(shù)據(jù)不一致,它是認為本地事務(wù)失敗還是繼續(xù)多次反查呢?

      反查接口的定義,它檢查的是本地事務(wù)(在我們這個例子里面就是數(shù)據(jù)庫事務(wù))有沒有執(zhí)行成功,并不會去比較數(shù)據(jù)是否一致。

      根據(jù)消息中的訂單ID,在訂單庫中查詢該訂單是否存在:

      存在,則返回成功

      否則,返回失敗

      RocketMQ會自動根據(jù)事務(wù)反查的結(jié)果提交或回滾事務(wù)消息。

      反查本地事務(wù)的實現(xiàn)并不依賴消息的發(fā)送方,即訂單服務(wù)的某節(jié)點的任何數(shù)據(jù)。

      這種情況下,即使發(fā)送事務(wù)消息的訂單服務(wù)節(jié)點宕機,RocketMQ依然可通過其他訂單服務(wù)節(jié)點執(zhí)行反查,確保事務(wù)完整性。

      5.1 RocketMQ事務(wù)消息流程圖

      若本地事務(wù)提交失敗,已發(fā)出去的消息又是無法撤回的,這就會導(dǎo)致數(shù)據(jù)不一致。

      5.2 FAQ

      因為消費失敗,會自動重試,所以不會丟消息,但可能重復(fù)消費。

      若Pro本地事務(wù)執(zhí)行太久還沒執(zhí)行完,消息中心就來反查是否有問題,所以可以將發(fā)消息放本地事務(wù)的后面吧,另外次數(shù)定義也是經(jīng)驗值。反查一般指定一個事務(wù)超時時間,超時之前會不定期反查。

      5.3 RocketMQ事務(wù)消息代碼實現(xiàn)案例

      訂單下單。

      在該方法里進行訂單創(chuàng)建,并提交本地事務(wù):

      若commit成功,則返回COMMIT狀態(tài)

      否則ROLLBACK狀態(tài)

      若正常返回COMMIT或ROLLBACK,不會存在第3步的反查情況。

      此方法會根據(jù)消息中的訂單號去數(shù)據(jù)庫確認訂單是否存在,存在就返回COMMIT狀態(tài),否則是ROLLBACK狀態(tài)。

      只要收到MQ消息就將本次訂單的商品從購物車中刪除即可。

      6 RocketMQ事務(wù)消息完整實現(xiàn)ACID了嗎

      A:本地事務(wù)的操作1,與往MQ中生產(chǎn)消息的操作2,是兩個分離操作,不具備原子性

      C:由于操作MQ屬異步,在數(shù)據(jù)一致性上,只能保證最終一致性。

      對時效性要求很高系統(tǒng),事務(wù)消息并非數(shù)據(jù)一致。但對時效性要求不高系統(tǒng),就是數(shù)據(jù)一致的。需要結(jié)合業(yè)務(wù)需要看問題

      I:由于事務(wù)消息分兩步操作,本地事務(wù)提交后,別的事務(wù)消息就已經(jīng)能看到提交的消息。所以,不符合隔離性

      D:rocketMq上支持事務(wù)的反查機制,但“半消息”存儲在磁盤還是內(nèi)存?

      若存儲在磁盤,那就支持持久性,即使事務(wù)消息提交后,發(fā)生服務(wù)突然宕機也不受影響

      若存儲在內(nèi)存,則無法保證持久性

      rocketmq實現(xiàn)分布式事務(wù),使用兩階段提交,和mysql寫redo log和binlog日志的兩階段提交類似,以訂單為例:

      提交訂單消息到mq中,等待mq回復(fù)ack,消息提交成功,但是此時的消息對消費組不可見,即half消息

      此階段像mysql的引擎層寫redo log的prepare階段。

      執(zhí)行本地事務(wù),執(zhí)行本地事務(wù)成功

      此階段像mysql的service層寫binlog的階段,寫binlog成功,最后提交或者回滾隊列事務(wù)。

      rocketmq為防止commit和rollback超時或者失敗,采取回查的補償機制,回查次數(shù)默認15次(感覺這個會不會導(dǎo)致服務(wù)超時了),超過會rollback,有點像mysql宕機重啟根據(jù)redo log中的xid找binlog的xid事務(wù),如果binlog日志也已經(jīng)寫成功,mysql這個事務(wù)也會提交,因為redo log和binlog這個事務(wù)都寫完整。

      消息對消費者不可見,將其消息的主題topic和隊列id修改為half topic,原先的主題和隊列id也做為消息的屬性,如果事務(wù)提交或者回滾會將其消息的隊列改為原先的隊列。rocketMq開啟任務(wù),從half topic中獲取消息,調(diào)用其中的生產(chǎn)者的監(jiān)聽進行回查是否提交回滾。

      rocketmq采用commitlog存放消息,消費者使用consumeQueue二級索引從commitlog獲取消息實體內(nèi)容。

      理解Index File:indexFile的作用就是給commitlog做的索引,提升讀取消息時的查詢效率。

      回查借助OP topic進行獲取到Half消息進行后續(xù)的回查操作。

      7 若MQ不支持半消息,是否有其他的解決方案

      利用數(shù)據(jù)庫的事務(wù)消息表。

      把消息信息的快照和對業(yè)務(wù)數(shù)據(jù)的操作作為數(shù)據(jù)庫事務(wù)操作數(shù)據(jù)庫,操作成功后從數(shù)據(jù)庫讀取消息信息發(fā)送給broker,收到發(fā)送成功的回執(zhí)后刪除數(shù)據(jù)庫中的消息快照。我個人覺得這種方案在不支持半消息的隊列方案里也是一種選擇,不知道您覺得這種實現(xiàn)方案有沒有什么問題。

      如果有個生產(chǎn)者和消費者都可訪問,并且性能還不錯的數(shù)據(jù)庫,肯定使用這個數(shù)據(jù)庫實現(xiàn)事務(wù)較好。

      然而大部分事務(wù)消息使用的場景是

      沒有這樣的數(shù)據(jù)庫

      或由于設(shè)計、安全或者網(wǎng)絡(luò)原因,生產(chǎn)者消費者不能共享數(shù)據(jù)庫

      或數(shù)據(jù)庫的性能達不到要求

      如果先創(chuàng)建訂單,當前服務(wù)由于不可抗拒因素不能正常工作,沒給購物車系統(tǒng)發(fā)送消息,這種情況加就會出現(xiàn):訂單已創(chuàng)建且購物車沒有清空。

      而發(fā)送半消息,可通過定期查詢事務(wù)狀態(tài)然后根據(jù)然后具體的業(yè)務(wù)回滾操作或者重新發(fā)送消息(保持業(yè)務(wù)的冪等性)。

      消費端做冪等處理來保障消息不會重復(fù)消費

      可以采用狀態(tài)機的方式

      消息數(shù)據(jù)唯一鍵+redis setnx來保障

      本地消息表,要確保插入本地消息表和執(zhí)行消息消費業(yè)務(wù)在同一事務(wù)里

      8 總結(jié)

      RocketMQ事務(wù)反查機制通過定期反查事務(wù)狀態(tài),來補償提交事務(wù)消息可能出現(xiàn)的通信失敗。

      在Kafka的事務(wù)功能中,并沒有類似的反查機制,需要用戶自行去解決這個問題。

      但不代表RocketMQ的事務(wù)功能比Kafka更好,只能說在該例場景,RocketMQ更適合。

      Kafka對事務(wù)的定義、實現(xiàn)和適用場景,和RocketMQ有較大差異。

      參考

      https://rocketmq.apache.org/docs/transaction-example/

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:word2013文檔中怎么添加表格(word文檔表格如何添加)
      下一篇:如何把65536條行增加到100萬條行
      相關(guān)文章
      亚洲国产精品激情在线观看| 亚洲a级成人片在线观看| 久久精品国产亚洲AV蜜臀色欲| 亚洲乱码无码永久不卡在线| 亚洲黄片毛片在线观看| 亚洲国产a级视频| 午夜亚洲国产精品福利| jizzjizz亚洲日本少妇| 午夜亚洲国产理论片二级港台二级| 亚洲久热无码av中文字幕| 亚洲午夜理论片在线观看| 国产精品高清视亚洲一区二区| 亚洲 日韩 色 图网站| 亚洲中文字幕一区精品自拍| 亚洲色少妇熟女11p| 亚洲乱码国产乱码精华| 亚洲AV无码男人的天堂| 亚洲日韩一中文字暮| 亚洲国产精品无码久久久秋霞1| 亚洲欧洲日产国码久在线| 亚洲av乱码一区二区三区按摩| 亚洲国产精品成人午夜在线观看| 亚洲sm另类一区二区三区| 苍井空亚洲精品AA片在线播放| 国产产在线精品亚洲AAVV| 亚洲成AV人在线观看网址| 亚洲一区二区三区国产精品| 伊人亚洲综合青草青草久热| 亚洲一区二区三区无码中文字幕| 亚洲精品成人片在线播放| 午夜亚洲国产理论秋霞| 91大神亚洲影视在线| 亚洲人成电影院在线观看| 亚洲中文精品久久久久久不卡| 亚洲一区二区无码偷拍| 日韩色日韩视频亚洲网站| 亚洲一级特黄大片无码毛片| 亚洲成A人片777777| 亚洲精品日韩专区silk| 欧洲 亚洲 国产图片综合| 亚洲aⅴ无码专区在线观看春色|