RabbitMQ如何解決被重複消費和資料丟失的問題?

架構師springboot發表於2018-11-05

RabbitMQ如何解決被重複消費和資料丟失的問題?
想想為什麼要使用MQ?

1.解耦,系統A在程式碼中直接呼叫系統B和系統C的程式碼,如果將來D系統接入,系統A還需要修改程式碼,過於麻煩!

2.非同步,將訊息寫入訊息佇列,非必要的業務邏輯以非同步的方式執行,加快響應速度

3.削峰,併發量大的時候,所有的請求直接懟到資料庫,造成資料庫連線異常

使用了訊息佇列會有什麼缺點?

1.系統可用性降低:你想啊,本來其他系統只要執行好好的,那你的系統就是正常的。現在你非要加個訊息佇列進去,那訊息佇列掛了,你的系統不是呵呵了。因此,系統可用性降低

2.系統複雜性增加:要多考慮很多方面的問題,比如一致性問題、如何保證訊息不被重複消費,如何保證保證訊息可靠傳輸。因此,需要考慮的東西更多,系統複雜性增大。

如何保證訊息佇列是高可用的?

使用叢集的方式維持MQ的可高用性。

如何保證訊息不被重複消費?

保證訊息不被重複消費的關鍵是保證訊息佇列的冪等性,這個問題針對業務場景來答分以下幾點:

1.比如,你拿到這個訊息做資料庫的insert操作。那就容易了,給這個訊息做一個唯一主鍵,那麼就算出現重複消費的情況,就會導致主鍵衝突,避免資料庫出現髒資料。

2.再比如,你拿到這個訊息做redis的set的操作,那就容易了,不用解決,因為你無論set幾次結果都是一樣的,set操作本來就算冪等操作。

3.如果上面兩種情況還不行,上大招。準備一個第三方介質,來做消費記錄。以redis為例,給訊息分配一個全域性id,只要消費過該訊息,將<id,message>以K-V形式寫入redis。那消費者開始消費前,先去redis中查詢有沒消費記錄即可。

如何解決丟資料的問題?

1.生產者丟資料

生產者的訊息沒有投遞到MQ中怎麼辦?從生產者弄丟資料這個角度來看,RabbitMQ提供transaction和confirm模式來確保生產者不丟訊息。

transaction機制就是說,傳送訊息前,開啟事物(channel.txSelect()),然後傳送訊息,如果傳送過程中出現什麼異常,事物就會回滾(channel.txRollback()),如果傳送成功則提交事物(channel.txCommit())。

然而缺點就是吞吐量下降了。因此,按照博主的經驗,生產上用confirm模式的居多。一旦channel進入confirm模式,所有在該通道上面釋出的訊息都將會被指派一個唯一的ID(從1開始),一旦訊息被投遞到所有匹配的佇列之後,rabbitMQ就會傳送一個Ack給生產者(包含訊息的唯一ID),這就使得生產者知道訊息已經正確到達目的佇列了.如果rabiitMQ沒能處理該訊息,則會傳送一個Nack訊息給你,你可以進行重試操作。

2.訊息佇列丟資料

處理訊息佇列丟資料的情況,一般是開啟持久化磁碟的配置。這個持久化配置可以和confirm機制配合使用,你可以在訊息持久化磁碟後,再給生產者傳送一個Ack訊號。這樣,如果訊息持久化磁碟之前,rabbitMQ陣亡了,那麼生產者收不到Ack訊號,生產者會自動重發。

那麼如何持久化呢,這裡順便說一下吧,其實也很容易,就下面兩步

①、將queue的持久化標識durable設定為true,則代表是一個持久的佇列

②、傳送訊息的時候將deliveryMode=2

這樣設定以後,rabbitMQ就算掛了,重啟後也能恢復資料。在訊息還沒有持久化到硬碟時,可能服務已經死掉,這種情況可以通過引入mirrored-queue即映象佇列,但也不能保證訊息百分百不丟失(整個叢集都掛掉)

3.消費者丟資料

啟用手動確認模式可以解決這個問題

①自動確認模式,消費者掛掉,待ack的訊息迴歸到佇列中。消費者丟擲異常,訊息會不斷的被重發,直到處理成功。不會丟失訊息,即便服務掛掉,沒有處理完成的訊息會重回佇列,但是異常會讓訊息不斷重試。

②手動確認模式,如果消費者來不及處理就死掉時,沒有響應ack時會重複傳送一條資訊給其他消費者;如果監聽程式處理異常了,且未對異常進行捕獲,會一直重複接收訊息,然後一直拋異常;如果對異常進行了捕獲,但是沒有在finally裡ack,也會一直重複傳送訊息(重試機制)。

③不確認模式,acknowledge="none" 不使用確認機制,只要訊息傳送完成會立即在佇列移除,無論客戶端異常還是斷開,只要傳送完就移除,不會重發。

如何保證訊息的順序性?

針對這個問題,通過某種演算法,將需要保持先後順序的訊息放到同一個訊息佇列中。然後只用一個消費者去消費該佇列。同一個queue裡的訊息一定是順序訊息的。我的觀點是保證入隊有序就行,出隊以後的順序交給消費者自己去保證,沒有固定套路。例如B訊息的業務應該保證在A訊息後業務後執行,那麼我們保證A訊息先進queueA,B訊息後進queueB就可以了。

加Java架構師進階交流群獲取Java工程化、高效能及分散式、高效能、深入淺出。高架構。效能調優、Spring,MyBatis,Netty原始碼分析和大資料等多個知識點高階進階乾貨的直播免費學習許可權 都是大牛帶飛 讓你少走很多的彎路的 群..號是:855801563 對了 小白勿進 最好是有開發經驗

注:加群要求

1、具有工作經驗的,面對目前流行的技術不知從何下手,需要突破技術瓶頸的可以加。

2、在公司待久了,過得很安逸,但跳槽時面試碰壁。需要在短時間內進修、跳槽拿高薪的可以加。

3、如果沒有工作經驗,但基礎非常紮實,對java工作機制,常用設計思想,常用java開發框架掌握熟練的,可以加。

4、覺得自己很牛B,一般需求都能搞定。但是所學的知識點沒有系統化,很難在技術領域繼續突破的可以加。

5.阿里Java高階大牛直播講解知識點,分享知識,多年工作經驗的梳理和總結,帶著大家全面、科學地建立自己的技術體系和技術認知!

相關文章