RabbitMQ面試問答(子文章)(持續更新)

-----> 總文章 入口

RabbitMQ

1、爲什麼要引入MQ系統,直接讀寫數據庫不行嗎?

其實就是問問你消息隊列都有哪些使用場景,然後你項目裏具體是什麼場景,說說你在這個場景裏用消息隊列是什麼?

面試官問你這個問題,期望的一個回答是說,你們公司有個什麼業務場景,這個業務場景有個什麼技術挑戰,如果不用 MQ 可能會很麻煩,但是你現在用了 MQ 之後帶給了你很多的好處。

先說一下消息隊列常見的使用場景吧,其實場景有很多,但是比較核心的有 3 個:解耦、異步、削峯。

解耦:多系統多進程的數據交換,用pub/sub (發佈/訂閱) .
.
異步:把大數據量的同步處理改爲異步.
.
削峯:一般的A 系統使用 MySQL,扛到每秒 2k 個請求就差不多了,如果每秒請求到 5k 的話,可能就直接把 MySQL 給打死了,導致系統崩潰,用戶也就沒法再使用系統了。如果使用 MQ, 每秒 5k 個請求寫入 MQ,A 系統每秒鐘最多處理 2k 個請求,因爲 MySQL 每秒鐘最多處理 2k 個。A 系統從 MQ 中慢慢拉取請求,每秒鐘就拉取 2k 個請求,不要超過自己每秒能處理的最 大請求數量就 ok,這樣下來,哪怕是高峯期的時候,A 系統也絕對不會掛掉,這又設計請求排隊的問題。

2、消息隊列有什麼優缺點?

優點:

解耦、異步、削峯

缺點:

系統可用性降低
系統引入的外部依賴越多,越容易掛掉。本來你就是 A 系統調用 BCD 三個系統的接口就好了,人 ABCD 四個系統好好的,沒啥問題,你偏加個 MQ 進來,萬一 MQ 掛了咋整,MQ 一掛,整套 系統崩潰的,你不就完了?
.
系統複雜度提高
硬生生加個 MQ 進來,你怎麼保證消息沒有重複消費?怎麼處理消息丟失的情況?怎麼保證消息傳遞的順序性?頭大頭大,問題一大堆,痛苦不已。
.
一致性問題
A 系統處理完了直接返回成功了,人都以爲你這個請求就成功了;但是問題是,要是 BCD 三個系統那裏,BD 兩個系統寫庫成功了,結果 C 系統寫庫失敗了,咋整?你這數據就不一致了。

3、Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什麼優缺點?

https://blog.csdn.net/Dome_/article/details/84990563

4、RabbitMQ 的高可用性如何保證?

相關鏈接: www.javazhiyin.com/22905.html

RabbitMQ 有三種模式:單機模式、普通集羣模式、鏡像集羣模式

單機模式

單機模式不存在高可用。

普通集羣模式

普通集羣模式也不存在高可用性,意思就是在多臺機器上啓動多個 RabbitMQ 實例,每個機器啓動一個。但是你創建的 queue,只會放在一個 RabbitMQ 實例上,但是每個實例都同步 queue 的元數據(元數據可以認爲是 queue 的一些配置信息,通過元數據,可以找到 queue 所在實例)。你消費的時候,實際上如果連接到了另外一個實例,那麼那個實例會從 queue 所在實例上
拉取數據過來。這種方式確實很麻煩,也不怎麼好,沒做到所謂的分佈式,就是個普通集羣。因爲這導致你要麼消費者每次隨機連接一個實例然後拉取數據,要麼固定連接那個
queue 所在實 例消費數據,前者有數據拉取的開銷,後者導致單實例性能瓶頸。而且如果那個放 queue 的實例宕機了,會導致接下來其他實例就無法從那個實例拉取,如果你開啓了消息持久化,讓 RabbitMQ 落地存儲消息的話,消息不一定會丟,得等這個實例恢復了,然後纔可以繼續從這個 queue 拉取數據。

鏡像集羣模式

鏡像集羣模式的策略是高可用策略,指定的時候可以要求數據同步到所有節點的,也可以要求同步到指定數量的節點,再次創建 queue
的時候,應用這個策略,就會自動將數據同步到其他的 節點上去了。

5、如何解決消息隊列的延時以及過期失效問題?

其實本質針對的場景,都是說,
可能你的消費端出了問題,不消費了;
或者消費的速度極其慢,造成消息堆積了,MQ存儲快要爆了,甚至開始過期失效刪除數據了。

針對這個問題可以有事前、事中、事後三種處理

事前:開發預警程序,監控最大的可堆積消息數,超過就發預警消息(比如短信),不要等出生產事故了再處理。

事中:看看消費端是不是故障停止了,緊急重啓。

事後:中華石杉老師就是說的這一種
點擊查看相關網址
需要對消費端緊急擴容,增加處理消費者進程,如擴充10倍處理,但其實這也有個問題,即數據庫的吞吐是有限制的,如果是消費到數據庫也是沒辦法巨量擴容的,所以還是要在吞吐能力支持下老老實實的泄洪消 費。所以事前預防還是最重要的。否則出發刪除過期數據,那就需要再重寫生產消息的程序,重新產生消息。

6、RabbitMQ如何保證不丟數據?

需要考慮3個可能丟數據的地方:生產端、隊列本身、消費端

生產端:

開啓事務(不推薦,太耗性能降低吞吐),推薦開啓 confirm 模式,在生產者那裏設置開啓 confirm 模式之後,你每次寫的消息都會分配一個唯一的 id,然後如果寫入了RabbitMQ 中,RabbitMQ 會給你回傳一個 ack 消息,告訴你說這個消息 ok 了。如果 RabbitMQ 沒能處理這個消息,會回調你的一個 nack 接口,告訴你這個消息接收失敗,你可以重試。而 且你可以結合這個機制自己在內存裏維護每個消息 id 的狀態,如果超過一定時間還沒接收到這個消息的回調,那麼你可以重發。

隊列本身:

就是 RabbitMQ 自己弄丟了數據,這個你必須開啓 RabbitMQ 的持久化,就是消息寫入之後會持久化到磁盤,哪怕是 RabbitMQ 自己掛了,恢復之後會自動讀取之前存儲的數據,一般數據不會丟。
設置持久化有兩個步驟:

  1. 創建 queue 的時候將其設置爲持久化,這樣就可以保證 RabbitMQ 持久化 queue 的元數據,但是它是不會持久化 queue 裏的數據的。
  2. 第二個是發送消息的時候將消息的 deliveryMode 設置爲 2。就是將消息設置爲持久化的,此時 RabbitMQ 就會將消息持久化到磁盤上去。

消費端:

其實和kafka的原理很類似,kafka即手動提交offsize。用RabbitMQ 提供的 ack 機制,簡單來說,就是你必須關閉 RabbitMQ 的自動 ack,通過自己的一個 api 來調用就行,然後每次你自己代碼裏確保處理完的時候,再在程序裏ack
這樣的話,如果你還沒處理完,不就沒有 ack 了?那 RabbitMQ 就認爲你還沒處理完,這個時候 RabbitMQ 會把這個消費分配給別 的 consumer 去處理,消息是不會丟的。

7、如何保證隊列的消息不被重複消費?

這個需要靈活作答,考察的是思考力,因爲消費的場景有很多,有數據庫、有緩存、有第三方接口

  1. 比如針對數據庫,你拿到這個消息做數據庫的insert操作。那就容易了,給這個消息做一個唯一主鍵(或者UUID),那麼就算出現重複消費的情況,就會導致主鍵衝突,避免數據庫出現髒數據。
  2. 再比如redis緩存,你拿到這個消息做redis的set的操作,那就容易了,不用解決,因爲你無論set幾次結果都是一樣的,set操作本來就算冪等操作。
  3. 再比如第三方接口,需要確定兩點,第三方接口程序是有去重能力的,那麼髒一點直接丟數據過去,如果沒有去重能力,還是需要我們來寫程序去重,就是第2點的辦法。

8、集羣節點類型都有什麼?

節點的存儲類型分爲兩種:磁盤節點 ,內存節點

磁盤節點就是配置信息和元信息存儲在磁盤上,內存節點把這些信息存儲在內存中,當然內次節點的性能是大大超越磁盤節點的。
單節點系統必須是磁盤節點,否則每次你重啓RabbitMQ之後所有的系統配置信息都會丟失。
RabbitMQ要求集羣中至少有一個磁盤節點,當節點加入和離開集羣時,必須通知磁盤節點。

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