RabbitMQ面试题及答案
什么是RabbitMQ?為什么使用RabbitMQ?
答:采用AMQP高級消息隊列協議的一種消息隊列技術,最大的特點就是消費并不需要確保提供方存在,實現了服務之間的高度解耦
可以用它來:解耦、異步、削峰。
為什么要使用rabbitmq
使用rabbitmq的場景
- 服務間異步通信
- 順序消費
- 定時任務
- 請求削峰
RabbitMQ有什么優缺點?
優點:
- 解耦
-
系統A在代碼中直接調用系統B和系統C的代碼,如果將來D系統接入,系統A還需要修改代碼,過于麻煩!
- 異步
-
將消息寫入消息隊列,非必要的業務邏輯以異步的方式運行,加快響應速度
- 削峰
-
并發量大的時候,所有的請求直接懟到數據庫,造成數據庫連接異常
缺點:
-
降低了系統的穩定性
-
本來系統運行好好的,現在你非要加入個消息隊列進去,那消息隊列掛了,你的系統不是呵呵了。因此,系統可用性會降低
-
增加了系統的復雜性
-
加入了消息隊列,要多考慮很多方面的問題,比如:一致性問題、如何保證消息不被重復消費、如何保證消息可靠性傳輸等。因此,需要考慮的東西更多,復雜性增大
線程Queue,進程Queue和RabbitMQ區別
- 進程Queue用于父進程與子進程(或同一父進程中多個子進程)間數據傳遞
- python自己的多個進程間交換數據或者與其他語言(如Java)進程queue就無能為力
- RabbitMQ就是這樣一個可以在不同程序間共享數據的代理
RabbitMQ 中的 broker 是指什么?cluster 又是指什么?
- broker 是指一個或多個 erlang node 的邏輯分組,且 node 上運行著 RabbitMQ 應用程序
- cluster 是在 broker 的基礎之上,增加了 node 之間共享元數據的約束
RabbitMQ 概念里的 channel、exchange 和 queue 是邏輯概念,還是對應著進程實體?分別起什么作用?
- queue 具有自己的 erlang 進程
- exchange 內部實現為保存 binding 關系的查找表
- channel 是實際進行路由工作的實體,即負責按照 routing_key 將 message 投遞給 queue
由 AMQP 協議描述可知,channel 是真實 TCP 連接之上的虛擬連接,所有 AMQP 命令都是通過 channel 發送的,且每一個 channel 有唯一的 ID。一個 channel 只能被單獨一個操作系統線程使用,故投遞到特定 channel 上的 message 是有順序的。但一個操作系統線程上允許使用多個 channel
消息基于什么傳輸?
由于TCP連接的創建和銷毀開銷較大,且并發數受系統資源限制,會造成性能瓶頸。RabbitMQ使用信道的方式來傳輸數據。信道是建立在真實的TCP連接內的虛擬連接,且每條TCP連接上的信道數量沒有限制
消息如何分發?
若該隊列至少有一個消費者訂閱,消息將以循環(round-robin)的方式發送給消費者。每條消息只會分發給一個訂閱的消費者(前提是消費者能夠正常處理消息并進行確認)
如何確保消息正確地發送至RabbitMQ?
- RabbitMQ使用發送方確認模式,確保消息正確地發送到RabbitMQ
- 發送方確認模式:
- 將信道設置成confirm模式(發送方確認模式),則所有在信道上發布的消息都會被指派一個唯一的ID
- 一旦消息被投遞到目的隊列后,或者消息被寫入磁盤后(可持久化的消息),信道會發送一個確認給生產者(包含消息唯一ID)
- 如果RabbitMQ發生內部錯誤從而導致消息丟失,會發送一條nack(not acknowledged,未確認)消息
- 發送方確認模式是異步的,生產者應用程序在等待確認的同時,可以繼續發送消息。當確認消息到達生產者應用程序,生產者應用程序的回調方法就會被觸發來處理確認消息
如何確保消息接收方消費了消息?
- 接收方消息確認機制:消費者接收每一條消息后都必須進行確認(消息接收和消息確認是兩個不同操作)
- 只有消費者確認了消息,RabbitMQ才能安全地把消息從隊列中刪除
這里并沒有用到超時機制,RabbitMQ僅通過Consumer的連接中斷來確認是否需要重新發送消息 - 也就是說,只要連接不中斷,RabbitMQ給了Consumer足夠長的時間來處理消息
下面列出幾種特殊情況:
- 如果消費者接收到消息,在確認之前斷開了連接或取消訂閱,RabbitMQ會認為消息沒有被分發,然后重新分發給下一個訂閱的消費者。(可能存在消息重復消費的隱患,需要根據bizId去重)
- 如果消費者接收到消息卻沒有確認消息,連接也未斷開,則RabbitMQ認為該消費者繁忙,將不會給該消費者分發更多的消息
如何保證RabbitMQ的高可用?
答:沒有哪個項目會搭建一臺RabbitMQ服務器提供服務,風險太大;
如何保證RabbitMQ不被重復消費?
先說為什么會重復消費:正常情況下,消費者在消費消息的時候,消費完畢后,會發送一個確認消息給消息隊列,消息隊列就知道該消息被消費了,就會將該消息從消息隊列中刪除;
但是因為網絡傳輸等等故障,確認信息沒有傳送到消息隊列,導致消息隊列不知道自己已經消費過該消息了,再次將消息分發給其他的消費者
- 在消息生產時,MQ內部針對每條生產者發送的消息生成一個inner-msg-id,作為去重的依據(消息投遞失敗并重傳),避免重復的消息進入隊列
- 在消息消費時,要求消息體中必須要有一個bizId(對于同一業務全局唯一,如支付ID、訂單ID、帖子ID等)作為去重的依據,避免同一條消息被重復消費
- 保證消息的唯一性,就算是多次傳輸,不要讓消息的多次消費帶來影響;保證消息等冪性
-
在寫入消息隊列的數據做唯一標示,消費消息時,根據唯一標識判斷是否消費過
如何保證RabbitMQ消息的可靠傳輸?
答:消息不可靠的情況可能是消息丟失,劫持等原因;
丟失又分為:
- 生產者丟失消息
- 消息列表丟失消息
- 消費者丟失消息
生產者丟失消息:
從生產者弄丟數據這個角度來看,RabbitMQ提供transaction和confirm模式來確保生產者不丟消息;
transaction機制就是說:
- 發送消息前,開啟事務(channel.txSelect()),然后發送消息,如果發送過程中出現什么異常,事務就會回滾(channel.txRollback())
- 如果發送成功則提交事務(channel.txCommit())
- 這種方式有個缺點:吞吐量下降
confirm模式用的居多:一旦channel進入confirm模式,所有在該信道上發布的消息都將會被指派一個唯一的ID(從1開始),一旦消息被投遞到所有匹配的隊列之后;
rabbitMQ就會發送一個ACK給生產者(包含消息的唯一ID),這就使得生產者知道消息已經正確到達目的隊列了;
如果rabbitMQ沒能處理該消息,則會發送一個Nack消息給你,你可以進行重試操作。
消息列表丟失消息:
消息持久化。
處理消息隊列丟數據的情況,一般是開啟持久化磁盤的配置。
這個持久化配置可以和confirm機制配合使用,你可以在消息持久化磁盤后,再給生產者發送一個Ack信號。
這樣,如果消息持久化磁盤之前,rabbitMQ陣亡了,那么生產者收不到Ack信號,生產者會自動重發。
那么如何持久化呢?
這里順便說一下吧,其實也很容易,就下面兩步
將queue的持久化標識durable設置為true,則代表是一個持久的隊列
發送消息的時候將deliveryMode=2
這樣設置以后,即使rabbitMQ掛了,重啟后也能恢復數據
消費者丟失消息:
消費者丟數據一般是因為采用了自動確認消息模式,改為手動確認消息即可!
消費者在收到消息之后,處理消息之前,會自動回復RabbitMQ已收到消息;
如果這時處理消息失敗,就會丟失該消息;
解決方案:處理消息成功后,手動回復確認消息。
如何保證RabbitMQ消息的順序性?
單線程消費保證消息的順序性;對消息進行編號,消費者處理消息是根據編號處理消息
消息怎么路由?
從概念上來說,消息路由必須有三部分:交換器、路由、綁定
生產者把消息發布到交換器上;綁定決定了消息如何從路由器路由到特定的隊列;消息最終到達隊列,并被消費者接收。
消息發布到交換器時,消息將擁有一個路由鍵(routing key),在消息創建時設定。
通過隊列路由鍵,可以把隊列綁定到交換器上。
消息到達交換器后,RabbitMQ會將消息的路由鍵與隊列的路由鍵進行匹配(針對不同的交換器有不同的路由規則)。如果能夠匹配到隊列,則消息會投遞到相應隊列中;如果不能匹配到任何隊列,消息將進入 “黑洞”。
常用的交換器主要分為一下三種:
- direct:如果路由鍵完全匹配,消息就被投遞到相應的隊列
- fanout:如果交換器收到消息,將會廣播到所有綁定的隊列上
- topic:可以使來自不同源頭的消息能夠到達同一個隊列。使用topic交換器時,可以使用通配符。比如:“*” 匹配特定位置的任意文本, “.” 把路由鍵分為了幾部分,“#” 匹配所有規則等。
特別注意:發往topic交換器的消息不能隨意的設置選擇鍵(routing_key),必須是由"."隔開的一系列的標識符組成。
什么是元數據?元數據分為哪些類型?包括哪些內容?與 cluster 相關的元數據有哪些?元數據是如何保存的?元數據在 cluster 中是如何分布的?
在非 cluster 模式下:
元數據主要分為
- Queue 元數據(queue 名字和屬性等)
- Exchange元數據(exchange 名字、類型和屬性等)
- Binding 元數據(存放路由關系的查找表)
- Vhost元數據(vhost 范圍內針對前三者的名字空間約束和安全屬性設置)
在 cluster 模式下:
還包括 cluster 中 node 位置信息和 node 關系信息
元數據按照 erlang node 的類型確定是僅保存于 RAM 中,還是同時保存在 RAM 和 disk 上。元數據在 cluster 中是全 node 分布的。
在單node 系統和多 node 構成的 cluster 系統中聲明 queue、exchange ,以及進行 binding 會有什么不同?
當你在單 node 上聲明 queue 時,只要該 node 上相關元數據進行了變更,你就會得到 Queue.Declare-ok 回應;而在 cluster 上聲明 queue ,則要求 cluster 上的全部 node 都要進行元數據成功更新,才會得到 Queue.Declare-ok 回應。另外,若 node 類型為 RAM node 則變更的數據僅保存在內存中,若類型為 disk node 則還要變更保存在磁盤上的數據。
死信隊列&死信交換器:DLX 全稱(Dead-Letter-Exchange),稱之為死信交換器,當消息變成一個死信之后,如果這個消息所在的隊列存在x-dead-letter-exchange參數,那么它會被發送到x-dead-letter-exchange對應值的交換器上,這個交換器就稱之為死信交換器,與這個死信交換器綁定的隊列就是死信隊列
消息在什么時候會變成死信?
- 消息拒絕并且沒有設置重新入隊
- 消息過期
- 消息堆積,并且隊列達到最大長度,先入隊的消息會變成DL
總結
以上是生活随笔為你收集整理的RabbitMQ面试题及答案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL外键关联(一对多)MySQL连
- 下一篇: RabbitMQ原理RabbitMQ各组