RabbitMQ從入門到精通(經典面試題)

1、如何保證消息的可靠性傳輸(如何處理消息丟失的問題)?

1)生產者弄丟了數據
生產者將數據發送到rabbitmq的時候,可能數據就在半路給搞丟了,因爲網絡啥的問題,都有可能。

此時可以選擇用rabbitmq提供的事務功能,就是生產者發送數據之前開啓rabbitmq事務(channel.txSelect),然後發送消息,如果消息沒有成功被rabbitmq接收到,那麼生產者會收到異常報錯,此時就可以回滾事務(channel.txRollback),然後重試發送消息;如果收到了消息,那麼可以提交事務(channel.txCommit)。但是問題是,rabbitmq事務機制一搞,基本上吞吐量會下來,因爲太耗性能。

所以一般來說,如果你要確保說寫rabbitmq的消息別丟,可以開啓confirm模式,在生產者那裏設置開啓confirm模式之後,你每次寫的消息都會分配一個唯一的id,然後如果寫入了rabbitmq中,rabbitmq會給你回傳一個ack消息,告訴你說這個消息ok了。如果rabbitmq沒能處理這個消息,會回調你一個nack接口,告訴你這個消息接收失敗,你可以重試。而且你可以結合這個機制自己在內存裏維護每個消息id的狀態,如果超過一定時間還沒接收到這個消息的回調,那麼你可以重發。

事務機制和cnofirm機制最大的不同在於,事務機制是同步的,你提交一個事務之後會阻塞在那兒,但是confirm機制是異步的,你發送個消息之後就可以發送下一個消息,然後那個消息rabbitmq接收了之後會異步回調你一個接口通知你這個消息接收到了。
所以一般在生產者這塊避免數據丟失,都是用confirm機制的。

2)rabbitmq弄丟了數據

就是rabbitmq自己弄丟了數據,這個你必須開啓rabbitmq的持久化,就是消息寫入之後會持久化到磁盤,哪怕是rabbitmq自己掛了,恢復之後會自動讀取之前存儲的數據,一般數據不會丟。除非極其罕見的是,rabbitmq還沒持久化,自己就掛了,可能導致少量數據會丟失的,但是這個概率較小。

設置持久化有兩個步驟,第一個是創建queue的時候將其設置爲持久化的,這樣就可以保證rabbitmq持久化queue的元數據,但是不會持久化queue裏的數據;第二個是發送消息的時候將消息的deliveryMode設置爲2,就是將消息設置爲持久化的,此時rabbitmq就會將消息持久化到磁盤上去。必須要同時設置這兩個持久化纔行,rabbitmq哪怕是掛了,再次重啓,也會從磁盤上重啓恢復queue,恢復這個queue裏的數據。

而且持久化可以跟生產者那邊的confirm機制配合起來,只有消息被持久化到磁盤之後,纔會通知生產者ack了,所以哪怕是在持久化到磁盤之前,rabbitmq掛了,數據丟了,生產者收不到ack,你也是可以自己重發的。

哪怕是你給rabbitmq開啓了持久化機制,也有一種可能,就是這個消息寫到了rabbitmq中,但是還沒來得及持久化到磁盤上,結果不巧,此時rabbitmq掛了,就會導致內存裏的一點點數據會丟失。

3)消費端弄丟了數據

rabbitmq如果丟失了數據,主要是因爲你消費的時候,剛消費到,還沒處理,結果進程掛了,比如重啓了,那麼就尷尬了,rabbitmq認爲你都消費了,這數據就丟了。

這個時候得用rabbitmq提供的ack機制,簡單來說,就是你關閉rabbitmq自動ack,可以通過一個api來調用就行,然後每次你自己代碼裏確保處理完的時候,再程序裏ack一把。這樣的話,如果你還沒處理完,不就沒有ack?那rabbitmq就認爲你還沒處理完,這個時候rabbitmq會把這個消費分配給別的consumer去處理,消息是不會丟的。

2、如何保證消息隊列的高可用啊?

鏡像集羣模式

你創建的queue,無論元數據還是queue裏的消息都會存在於多個實例上,然後每次你寫消息到queue的時候,都會自動把消息到多個實例的queue裏進行消息同步。

這樣的話,好處在於,你任何一個機器宕機了,沒事兒,別的機器都可以用。壞處在於,第一,這個性能開銷也太大了吧,消息同步所有機器,導致網絡帶寬壓力和消耗很重!第二,這麼玩兒,就沒有擴展性可言了,如果某個queue負載很重,你加機器,新增的機器也包含了這個queue的所有數據,並沒有辦法線性擴展你的queue。

那麼怎麼開啓這個鏡像集羣模式呢?其實很簡單rabbitmq有很好的管理控制檯,就是在後臺新增一個策略,這個策略是鏡像集羣模式的策略,指定的時候可以要求數據同步到所有節點的,也可以要求就同步到指定數量的節點,然後你再次創建queue的時候,應用這個策略,就會自動將數據同步到其他的節點上去了。

在這裏插入圖片描述

3、如何保證消息不被重複消費啊(如何保證消息消費時的冪等性)?

其實還是得結合業務來思考,我這裏給幾個思路:

(1)比如你拿個數據要寫庫,你先根據主鍵查一下,如果這數據都有了,你就別插入了,update一下好吧

(2)比如你是寫redis,那沒問題了,反正每次都是set,天然冪等性

(3)比如你不是上面兩個場景,那做的稍微複雜一點,你需要讓生產者發送每條數據的時候,裏面加一個全局唯一的id,類似訂單id之類的東西,然後你這裏消費到了之後,先根據這個id去比如redis裏查一下,之前消費過嗎?如果沒有消費過,你就處理,然後這個id寫redis。如果消費過了,那你就別處理了,保證別重複處理相同的消息即可。

還有比如基於數據庫的唯一鍵來保證重複數據不會重複插入多條,我們之前線上系統就有這個問題,就是拿到數據的時候,每次重啓可能會有重複,因爲kafka消費者還沒來得及提交offset,重複數據拿到了以後我們插入的時候,因爲有唯一鍵約束了,所以重複數據只會插入報錯,不會導致數據庫中出現髒數據。

如何保證MQ的消費是冪等性的,需要結合具體的業務來看

4、如何保證消息的順序性?

先看看順序會錯亂場景

(1)rabbitmq:一個queue,多個consumer,這不明顯亂了。
在這裏插入圖片描述
(2)rabbitmq:或者就一個queue但是對應一個consumer。在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章