中間件面試專題:RabbitMQ高頻面試問題

開篇介紹面試

你們好,公衆號【Java極客思惟】近期會整理一些Java高頻面試題分享給小夥伴,也但願看到的小夥伴在找工做過程當中可以用獲得!本章節主要針對Java一些消息中間件高頻面試題進行分享。服務器

通知:公衆號【Java極客思惟】正在送書福利活動,關注公衆號並參加福利活動吧!只有參與了本次活動的小夥伴纔可以參與年末的大福利,不要錯過呀~微信

Q1:網絡

RabbitMQ 的介紹、用途、好處?異步

RabbitMQ是一款開源的,Erlang編寫的,基於AMQP協議的消息中間件。性能

做用:解耦 、 異步 、 削峯 。spa

優勢:解耦、異步、削峯;操作系統

缺點:下降了系統的穩定性:系統中使用了消息隊列,若是消息隊列掛了,那麼系統也會掛。下降了系統可用性。線程

加入消息隊列,要考慮不少方面的問題,好比:一致性問題 、如何保證消息不被重複消費 、 如何保證消息可靠性傳輸 等。所以考慮的因素有不少方面,複雜性增長。設計

Q2:

RabbitMQ 包括哪些要素?

  • 生產者 :消息的建立者,發送到RabbitMQ
  • 消費者 :鏈接到RabbitMQ,訂閱到隊列上,消費消息,持續訂閱(basicConsumer)和單條訂閱(basicGet)
  • 消息 :包含有效載荷和標籤,有效載荷指要傳輸的數據,標籤描述了有效載荷,而且RabbitMQ用它來決定誰得到消息,消費者只能拿到有效載荷,並不知道生產者是誰。

Q3:

RabbitMQ 什麼是信道?

信道:是生產者、消費者與RabbitMQ通訊的渠道,生產者publish或是消費者subscribe一個隊列都是經過信道來通訊的。信道是創建在TCP鏈接上的虛擬鏈接。就是說RabbitMQ在一條TCP上創建成百上千個信道來達到多個線程處理,這個TCP被多個線程共享,每一個線程對應一個信道,信道在RabbitMQ都有一個惟一的ID,保證了信道私有性,對應上惟一的線程使用。

疑問:爲何不創建多個TCP鏈接?

緣由是RabbitMQ須要保證性能,系統爲每一個線程開闢一個TCP是很是消耗性能的,美妙成百上千的創建銷燬TCP會嚴重消耗系統性能;因此RabbitMQ選擇創建多個信道(創建在TCP的虛擬鏈接)鏈接到RabbitMQ上

Q4:

RabbitMQ概念裏的channel、exchange 和 queue是邏輯概念,仍是對應着進程實體?做用分別是什麼?

queue 具備本身的 erlang 進程;

exchange 內部實現爲保存 binding 關係的查找表;

channel 是實際進行路由工做的實體,負責按照 routing_key 將 message投遞給queue。

由 AMQP 協議描述可知,channel 是真實TCP鏈接之上的 虛擬鏈接 , 全部AMQP 命令都是經過 channel 發送的,且每個 channel 有 惟一的ID 。一個 channel 只能被單獨一個操做系統線程使用,因此投遞到特定的 channel 上的 message 是有順序的。單一個操做系統線程上容許使用多個channel。

Q5:

RabbitMQ消息是如何路由的?

消息路由必須有三部分:交換器路由綁定

生產者把消息發佈到交換器上,綁定決定了消息如何從路由器路由到特定的隊列;消息最終到達隊列,並被消費者接收。

消息發佈到交換器時,消息將擁有一個 路由鍵(routing key) , 在消息建立時設定。

經過隊列路由鍵,能夠把隊列綁定到交換器上。

消息到達交換器後,RabbitMQ會將消息的路由鍵與隊列的路由鍵進行匹配(針對不一樣的交換器有不一樣的路由規則)。若是可以匹配到隊列,則消息會投遞到相應隊列中;若是不能匹配到任何隊列,消息將進入"黑洞"。

經常使用的交換器主要分爲如下三種:

  • direct :若是路由鍵徹底匹配,消息就會被投遞到相應的隊列;每一個AMQP的實現都必須有一個direct交換器,包含一個空白字符串名稱的默認交換器。聲明一個隊列時,會自動綁定到默認交換器,而且以隊列名稱做爲路由鍵:channel -> basic_public($msg, '', 'queue-name')
  • fanout : 若是交換器收到消息,將會廣播到全部綁定的隊列上;
  • topic :可使來自不一樣源頭的消息可以到達同一個隊列。使用topic交換器時,可使用通配符,好比:"*" 匹配特定位置的任意文本,"." 把路由鍵分爲了幾個標識符, "#" 匹配全部規則等。

特別注意:發往topic交換器的消息不能隨意的設置選擇鍵(routing_key),必須是有"."隔開的一系列的標識符組成。

Q6:

RabbitMQ消息確認過程?

      消費者收到的每一條消息都必須進行確認(自動確認和自行確認)

        消費者在聲明隊列時,能夠置頂autoAck參數,當autoAck = false時,RabbitMQ會等待消費者顯式發送回 ack 信號後才從內存(和磁盤,若是是持久化消息的話)中刪除消息,不然RabbitMQ會在隊列中消息被消費後當即刪除它。

        採用消息確認機制後,只要使 autoAck = false,消費者就有足夠的時間處理消息(任務),不用擔憂處理消息過程當中消費者進程掛掉後消息丟失的問題,由於RabbitMQ會一直持有消息直到消費者顯式調用basicAck爲止。

        當autoAck = false時,對於RabbitMQ服務器端而言,隊列中的消息分紅了兩部分:一部分是等待投遞給消費者的消息;一部分是已經投遞給消費者,可是尚未收到消費者ack信號的消息。若是服務器端一直沒有收到消費者的ack信號,而且消費此消息的消費者已經斷開鏈接,則服務器端會安排該消息 從新進入隊列,等待投遞給下一個消費者(也可能仍是原來的那個消費者)。

        RabbitMQ不會爲 ack消息設置超時時間,它判斷此消息是否須要從新投遞給消費者的惟一依據是消費該消息的消費者鏈接是否已經斷開。這麼設計的緣由是RabbitMQ容許消費者消費一條消息的時間能夠好久好久。

Q7:

如何保證RabbitMQ不被重複消費?

正常狀況下,消費者在消費消息的時候,消費完畢後,會發送一個確認信息給消息隊列,消息隊列就知道該消息被消費了,就會將該消息從消息隊列中刪除。

可是由於網絡傳輸等故障,確認信息沒有傳送到消息隊列,致使消息隊列不知道本身已經消費過該消息了,再次將消息分發給其餘的消費者。

解決思路:

保證消息的惟一性,就算是屢次傳輸,不要讓消息的屢次消費帶來影響;

保證消息冪等性;

好比:在寫入消息隊列的數據作惟一標識,消費消息時,根據惟一標識判斷該消息是否被消費過。

Q8:

如何保證RabbitMQ消息的可靠傳輸?

消息不可靠的狀況多是消息丟失,劫持等緣由;

丟失可能又分爲:

  • 生產者丟失消息
  • 消息隊列丟失消息
  • 消費者丟失消息

生產者丟失消息

從生產者弄丟數據來看,RabbitMQ提供了 transaction 機制 和 confirm 模式 來確保生產者不丟失消息;

  • transaction機制: 發送消息前,開啓事務(channel.exSelect()),而後發送消息,若是發送過程當中出現異常,事務就會回滾(channel.txRollback()),若是發送成功則提交事務(channel.txCommit())。
  • confirm模式:通常這種模式居多,一旦channel進入confirm模式,全部在該信道上發佈的消息都將會被指派一個惟一的ID(從1開始),一旦消息被投遞到全部匹配的隊列後;RabbitMQ就會發送一個ACK給生產者(包含消息的惟一ID),這就使得生產者知道消息已經正確到達目的隊列了。

若是RabbitMQ沒能處理該消息,則會發送一個Nack消息回來,這樣能夠進行重試操做。

消息隊列丟失消息

針對消息隊列丟失數據的狀況,通常是開啓持久化磁盤的配置:

將隊列的持久化標識 durable 設置爲 true , 則表明是一個持久的隊列,發送消息的時候講 deliveryMode=2 這樣設置之後,即便RabbitMQ掛了,重啓後也能恢復數據。

消費者丟失消息

消費者丟失消息通常是由於採用了自動確認消息模式,改成手動確認消息便可。

消費者在收到消息以後,處理消息以前,會自動回覆RabbitMQ已收到消息;若是這時候處理消息失敗,就會丟失該消息;

解決方案:處理消息成功後,手動回覆確認消息。


點關注、不迷路

若是以爲文章不錯,歡迎關注點贊收藏,大家的支持是我創做的動力,感謝你們。

若是文章寫的有問題,請不要吝嗇,歡迎留言指出,我會及時覈查修改。

若是你還想更加深刻的瞭解我,能夠微信搜索「Java極客思惟」進行關注。天天8:00準時推送技術文章,讓你的上班路不在孤獨,並且每個月還有送書活動,助你提高硬實力!

相關文章
相關標籤/搜索