RabbitMQ面試題

一、爲何要引入MQ系統,直接讀寫數據庫不行嗎?
其實就是問問你消息隊列都有哪些使用場景,而後你項目裏具體是什麼場景,說說你在這個場景裏用消息隊列是什麼?html

面試官問你這個問題,指望的一個回答是說,大家公司有個什麼業務場景,這個業務場景有個什麼技術挑戰,若是不用 MQ 可能會很麻煩,可是你如今用了 MQ 以後帶給了你不少的好處。java

先說一下消息隊列常見的使用場景吧,其實場景有不少,可是比較核心的有 3 個:解耦、異步、削峯。
解耦:多系統多進程的數據交換,用pub/sub
異步:把大數據量的同步處理改成異步
削峯:通常的A 系統使用 MySQL,扛到每秒 2k 個請求就差很少了,若是每秒請求到 5k 的話,可能就直接把 MySQL 給打死了,致使系統崩潰,用戶也就無法再使用系統了。若是使用 MQ, 每秒 5k 個請求寫入 MQ,A 系統每秒鐘最多處理 2k 個請求,由於 MySQL 每秒鐘最多處理 2k 個。A 系統從 MQ 中慢慢拉取請求,每秒鐘就拉取 2k 個請求,不要超過本身每秒能處理的最 大請求數量就 ok,這樣下來,哪怕是高峯期的時候,A 系統也絕對不會掛掉,這又設計請求排隊的問題。git

二、消息隊列有什麼優缺點?
優勢:解耦、異步、削峯
缺點:
系統可用性下降
系統引入的外部依賴越多,越容易掛掉。原本你就是 A 系統調用 BCD 三個系統的接口就行了,人 ABCD 四個系統好好的,沒啥問題,你偏加個 MQ 進來,萬一 MQ 掛了咋整,MQ 一掛,整套
系統崩潰的,你不就完了?github

系統複雜度提升
硬生生加個 MQ 進來,你怎麼保證消息沒有重複消費?怎麼處理消息丟失的狀況?怎麼保證消息傳遞的順序性?頭大頭大,問題一大堆,痛苦不已。面試

一致性問題
A 系統處理完了直接返回成功了,人都覺得你這個請求就成功了;可是問題是,要是 BCD 三個系統那裏,BD 兩個系統寫庫成功了,結果 C 系統寫庫失敗了,咋整?你這數據就不一致了。redis

三、Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什麼優缺點?
https://blog.csdn.net/Dome_/article/details/84990563數據庫

四、RabbitMQ 的高可用性如何保證?
RabbitMQ 有三種模式:單機模式、普通集羣模式、鏡像集羣模式
單機模式不存在高可用。
普通集羣模式也不存在高可用性,意思就是在多臺機器上啓動多個 RabbitMQ 實例,每一個機器啓動一個。可是你建立的 queue,只會放在一個 RabbitMQ 實例上,可是每一個實例都同步 queue 的元數據(元數據能夠認爲是 queue 的一些配置信息,經過元數據,能夠找到 queue 所在實例)。你消費的時候,實際上若是鏈接到了另一個實例,那麼那個實例會從 queue 所在實例上 拉取數據過來。這種方式確實很麻煩,也不怎麼好,沒作到所謂的分佈式,就是個普通集羣。由於這致使你要麼消費者每次隨機鏈接一個實例而後拉取數據,要麼固定鏈接那個 queue 所在實 例消費數據,前者有數據拉取的開銷,後者致使單實例性能瓶頸。並且若是那個放 queue 的實例宕機了,會致使接下來其餘實例就沒法從那個實例拉取,若是你開啓了消息持久化,讓 RabbitMQ 落地存儲消息的話,消息不必定會丟,得等這個實例恢復了,而後才能夠繼續從這個 queue 拉取數據。
鏡像集羣模式的策略是高可用策略,指定的時候能夠要求數據同步到全部節點的,也能夠要求同步到指定數量的節點,再次建立 queue 的時候,應用這個策略,就會自動將數據同步到其餘的 節點上去了。api

https://www.javazhiyin.com/22905.html緩存

五、如何解決消息隊列的延時以及過時失效問題?
其實本質針對的場景,都是說,可能你的消費端出了問題,不消費了;或者消費的速度極其慢,形成消息堆積了,MQ存儲快要爆了,甚至開始過時失效刪除數據了。異步

針對這個問題能夠有事前、事中、過後三種處理

  • 事前:開發預警程序,監控最大的可堆積消息數,超過就發預警消息(好比短信),不要等出生產事故了再處理。
  • 事中:看看消費端是否是故障中止了,緊急重啓。
  • 過後:中華石杉老師就是說的這一種(https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/mq-time-delay-and-expired-failure.md),須要對消費端緊急擴容 ,增長處理消費者進程,如擴充10倍處理,但其實這也有個問題,即數據庫的吞吐是有限制的,若是是消費到數據庫也是沒辦法巨量擴容的,因此仍是要在吞吐能力支持下老老實實的泄洪消 費。因此事前預防仍是最重要的。不然出發刪除過時數據,那就須要再重寫生產消息的程序,從新產生消息。

六、RabbitMQ如何保證不丟數據?
須要考慮3個可能丟數據的地方:生產端、隊列自己、消費端

  • 6.1生產端:開啓事務(不推薦,太耗性能下降吞吐),推薦開啓 confirm 模式,在生產者那裏設置開啓 confirm 模式以後,你每次寫的消息都會分配一個惟一的 id,而後若是寫入了RabbitMQ 中,RabbitMQ 會給你回傳一個 ack 消息,告訴你說這個消息 ok 了。若是 RabbitMQ 沒能處理這個消息,會回調你的一個 nack 接口,告訴你這個消息接收失敗,你能夠重試。而 且你能夠結合這個機制本身在內存裏維護每一個消息 id 的狀態,若是超過必定時間還沒接收到這個消息的回調,那麼你能夠重發。
  • 6.2隊列自己:就是 RabbitMQ 本身弄丟了數據,這個你必須開啓 RabbitMQ 的持久化,就是消息寫入以後會持久化到磁盤,哪怕是 RabbitMQ 本身掛了,恢復以後會自動讀取以前存儲的數據,通常數據不會丟。

設置持久化有兩個步驟:

    •  建立 queue 的時候將其設置爲持久化,這樣就能夠保證 RabbitMQ 持久化 queue 的元數據,可是它是不會持久化 queue 裏的數據的。
    •  第二個是發送消息的時候將消息的 deliveryMode 設置爲 2。就是將消息設置爲持久化的,此時 RabbitMQ 就會將消息持久化到磁盤上去。
  • 6.3消費端:其實和kafka的原理很相似,kafka即手動提交offsize。用RabbitMQ 提供的 ack 機制,簡單來講,就是你必須關閉 RabbitMQ 的自動 ack,經過本身的一個 api 來調用就行,而後每次你本身代碼裏確保處理完的時候,再在程序裏 ack。這樣的話,若是你還沒處理完,不就沒有 ack 了?那 RabbitMQ 就認爲你還沒處理完,這個時候 RabbitMQ 會把這個消費分配給別 的 consumer 去處理,消息是不會丟的。

七、如何保證隊列的消息不被重複消費?
這個須要靈活做答,考察的是思考力,由於消費的場景有不少,有數據庫、有緩存、有第三方接口

  • 1.好比針對數據庫,你拿到這個消息作數據庫的insert操做。那就容易了,給這個消息作一個惟一主鍵(或者UUID),那麼就算出現重複消費的狀況,就會致使主鍵衝突,避免數據庫出現髒數據。
  • 2.再好比redis緩存,你拿到這個消息作redis的set的操做,那就容易了,不用解決,由於你不管set幾回結果都是同樣的,set操做原本就算冪等操做。
  • 3.再好比第三方接口,須要肯定兩點,第三方接口程序是有去重能力的,那麼髒一點直接丟數據過去,若是沒有去重能力,仍是須要咱們來寫程序去重,就是第2點的辦法。

八、集羣節點類型都有什麼?
節點的存儲類型分爲兩種:

  • 磁盤節點
  • 內存節點

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

相關文章
相關標籤/搜索