RabbitMQ—重复消费、数据丢失和消息顺序性
原文作者:weixin_49367803
原文地址:https://blog.csdn.net/weixin_49367803/article/details/108480256
一、如何保證消息不被重復消費?
1、保證消息不被重復消費的關鍵是保證消息隊列的冪等性,這個問題針對業務場景來答分以下幾點:
- 比如,你拿到這個消息做數據庫的insert操作。那就容易了,給這個消息做一個唯一主鍵,那么就算出現重復消費的情況,就會導致主鍵沖突,避免數據庫出現臟數據。
- 再比如,你拿到這個消息做redis的set的操作,那就容易了,不用解決,因為你無論set幾次結果都是一樣的,set操作本來就算冪等操作。
- 如果上面兩種情況還不行,上大招。準備一個第三方介質,來做消費記錄。以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就可以了。
總結
以上是生活随笔為你收集整理的RabbitMQ—重复消费、数据丢失和消息顺序性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python之列表list模块
- 下一篇: RobotFramework自动化框架—