RabbitMQ面試題集錦(精選)(另附思維導(dǎo)圖)

RabbitMQ面試題集錦(精選)

1.使用RabbitMQ有什么好處?

1.解耦,系統(tǒng)A在代碼中直接調(diào)用系統(tǒng)B和系統(tǒng)C的代碼,如果將來D系統(tǒng)接入,系統(tǒng)A還需要修改代碼,過于麻煩!
2.異步,將消息寫入消息隊列,非必要的業(yè)務(wù)邏輯以異步的方式運行,加快響應(yīng)速度
3.削峰,并發(fā)量大的時候,所有的請求直接懟到數(shù)據(jù)庫,造成數(shù)據(jù)庫連接異常

2.RabbitMQ 中的 broker 是指什么?cluster 又是指什么?

broker 是指一個或多個 erlang node 的邏輯分組,且 node 上運行著 RabbitMQ 應(yīng)用程序。
cluster 是在 broker 的基礎(chǔ)之上,增加了 node 之間共享元數(shù)據(jù)的約束。

3. RabbitMQ 概念里的 channel、exchange 和 queue 是邏輯概念,還是對應(yīng)著進(jìn)程實體?分別起什么作用?

Queue 具有自己的 erlang 進(jìn)程;exchange 內(nèi)部實現(xiàn)為保存 binding 關(guān)系的查找表;channel 是實際進(jìn)行路由工作的實體,即負(fù)責(zé)按照 routing_key 將 message 投遞給 queue 。由 AMQP 協(xié)議描述可知,channel 是真實 TCP 連接之上的虛擬連接,所有 AMQP 命令都是通過 channel 發(fā)送的,且每一個 channel 有唯一的 ID。一個 channel 只能被單獨一個操作系統(tǒng)線程使用,故投遞到特定 channel 上的 message 是有順序的。但一個操作系統(tǒng)線程上允許使用多個 channel 。

4. vhost 是什么?起什么作用?

vhost 可以理解為虛擬 broker ,即 mini-RabbitMQ server。其內(nèi)部均含有獨立的 queue、exchangebinding 等,但最最重要的是,其擁有獨立的權(quán)限系統(tǒng),可以做到 vhost 范圍的用戶控制。當(dāng)然,從 RabbitMQ 的全局角度,vhost 可以作為不同權(quán)限隔離的手段(一個典型的例子就是不同的應(yīng)用可以跑在不同的 vhost 中)。

5. 消息基于什么傳輸?

由于TCP連接的創(chuàng)建和銷毀開銷較大,且并發(fā)數(shù)受系統(tǒng)資源限制,會造成性能瓶頸。RabbitMQ使用信道的方式來傳輸數(shù)據(jù)。信道是建立在真實的TCP連接內(nèi)的虛擬連接,且每條TCP連接上的信道數(shù)量沒有限制。

6. 消息如何分發(fā)?

若該隊列至少有一個消費者訂閱,消息將以循環(huán)(round-robin)的方式發(fā)送給消費者。每條消息只會分發(fā)給一個訂閱的消費者(前提是消費者能夠正常處理消息并進(jìn)行確認(rèn))。

7. 消息怎么路由?

從概念上來說,消息路由必須有三部分:交換器、路由綁定。生產(chǎn)者把消息發(fā)布到交換器上;綁定決定了消息如何從路由器路由到特定的隊列;消息最終到達(dá)隊列,并被消費者接收。

消息發(fā)布到交換器時,消息將擁有一個路由鍵(routing key),在消息創(chuàng)建時設(shè)定。
通過隊列路由鍵,可以把隊列綁定到交換器上。
消息到達(dá)交換器后,RabbitMQ會將消息的路由鍵與隊列的路由鍵進(jìn)行匹配(針對不同的交換器有不同的路由規(guī)則)。如果能夠匹配到隊列,則消息會投遞到相應(yīng)隊列中;如果不能匹配到任何隊列,消息將進(jìn)入 “黑洞”。

常用的交換器主要分為一下三種:

  • direct:如果路由鍵完全匹配,消息就被投遞到相應(yīng)的隊列
  • fanout:如果交換器收到消息,將會廣播到所有綁定的隊列上
  • topic:可以使來自不同源頭的消息能夠到達(dá)同一個隊列。使用topic交換器時,可以使用通配符。
    比如:“*” 匹配特定位置的任意文本, “.” 把路由鍵分為了幾部分,“#” 匹配所有規(guī)則等。
    特別注意:發(fā)往topic交換器的消息不能隨意的設(shè)置選擇鍵(routing_key),必須是由"."隔開的一系列的標(biāo)識符組成。

8. 什么是元數(shù)據(jù)?元數(shù)據(jù)分為哪些類型?包括哪些內(nèi)容?與 cluster 相關(guān)的元數(shù)據(jù)有哪些?元數(shù)據(jù)是如何保存的?元數(shù)據(jù)在 cluster 中是如何分布的?

在非 cluster 模式下,元數(shù)據(jù)主要分為 Queue 元數(shù)據(jù)(queue 名字和屬性等)、Exchange元數(shù)據(jù)(exchange 名字、類型和屬性等)、Binding 元數(shù)據(jù)(存放路由關(guān)系的查找表)、Vhost元數(shù)據(jù)(vhost 范圍內(nèi)針對前三者的名字空間約束和安全屬性設(shè)置)。
cluster 模式下,還包括 cluster 中 node 位置信息和 node 關(guān)系信息。元數(shù)據(jù)按照 erlang node 的類型確定是僅保存于 RAM 中,還是同時保存在 RAM 和 disk 上。元數(shù)據(jù)在 cluster 中是全 node 分布的。

9. 在單node 系統(tǒng)和多 node 構(gòu)成的 cluster 系統(tǒng)中聲明 queue、exchange ,以及進(jìn)行 binding 會有什么不同?

答:當(dāng)你在單 node 上聲明 queue 時,只要該 node 上相關(guān)元數(shù)據(jù)進(jìn)行了變更,你就會得到 Queue.Declare-ok 回應(yīng);而在 cluster 上聲明 queue ,則要求 cluster 上的全部 node 都要進(jìn)行元數(shù)據(jù)成功更新,才會得到 Queue.Declare-ok 回應(yīng)。另外,若 node 類型為 RAM node 則變更的數(shù)據(jù)僅保存在內(nèi)存中,若類型為 disk node 則還要變更保存在磁盤上的數(shù)據(jù)。

死信隊列&死信交換器:DLX 全稱(Dead-Letter-Exchange),稱之為死信交換器,當(dāng)消息變成一個死信之后,如果這個消息所在的隊列存在x-dead-letter-exchange參數(shù),那么它會被發(fā)送到x-dead-letter-exchange對應(yīng)值的交換器上,這個交換器就稱之為死信交換器,與這個死信交換器綁定的隊列就是死信隊列。

10. 如何確保消息正確地發(fā)送至RabbitMQ?

RabbitMQ使用發(fā)送方確認(rèn)模式,確保消息正確地發(fā)送到RabbitMQ
發(fā)送方確認(rèn)模式:將信道設(shè)置成confirm模式(發(fā)送方確認(rèn)模式),則所有在信道上發(fā)布的消息都會被指派一個唯一的ID。一旦消息被投遞到目的隊列后,或者消息被寫入磁盤后(可持久化的消息),信道會發(fā)送一個確認(rèn)給生產(chǎn)者(包含消息唯一ID)。如果RabbitMQ發(fā)生內(nèi)部錯誤從而導(dǎo)致消息丟失,會發(fā)送一條nack(not acknowledged,未確認(rèn))消息。發(fā)送方確認(rèn)模式是異步的,生產(chǎn)者應(yīng)用程序在等待確認(rèn)的同時,可以繼續(xù)發(fā)送消息。當(dāng)確認(rèn)消息到達(dá)生產(chǎn)者應(yīng)用程序,生產(chǎn)者應(yīng)用程序的回調(diào)方法就會被觸發(fā)來處理確認(rèn)消息。

11. 如何確保消息接收方消費了消息?

接收方消息確認(rèn)機制:消費者接收每一條消息后都必須進(jìn)行確認(rèn)(消息接收和消息確認(rèn)是兩個不同操作)。只有消費者確認(rèn)了消息,RabbitMQ才能安全地把消息從隊列中刪除。這里并沒有用到超時機制,RabbitMQ僅通過Consumer的連接中斷來確認(rèn)是否需要重新發(fā)送消息。也就是說,只要連接不中斷,RabbitMQ給了Consumer足夠長的時間來處理消息。

下面羅列幾種特殊情況:

  • 如果消費者接收到消息,在確認(rèn)之前斷開了連接或取消訂閱,RabbitMQ會認(rèn)為消息沒有被分發(fā),然后重新分發(fā)給下一個訂閱的消費者。(可能存在消息重復(fù)消費的隱患,需要根據(jù)bizId去重)
  • 如果消費者接收到消息卻沒有確認(rèn)消息,連接也未斷開,則RabbitMQ認(rèn)為該消費者繁忙,將不會給該消費者分發(fā)更多的消息。

12. 如何避免消息重復(fù)投遞或重復(fù)消費?

在消息生產(chǎn)時,MQ內(nèi)部針對每條生產(chǎn)者發(fā)送的消息生成一個inner-msg-id,作為去重和冪等的依據(jù)(消息投遞失敗并重傳),避免重復(fù)的消息進(jìn)入隊列;在消息消費時,要求消息體中必須要有一個bizId(對于同一業(yè)務(wù)全局唯一,如支付ID、訂單ID、帖子ID等)作為去重和冪等的依據(jù),避免同一條消息被重復(fù)消費。

這個問題針對業(yè)務(wù)場景來答分以下幾點:

1.比如,你拿到這個消息做數(shù)據(jù)庫的insert操作。那就容易了,給這個消息做一個唯一主鍵,那么就算出現(xiàn)重復(fù)消費的情況,就會導(dǎo)致主鍵沖突,避免數(shù)據(jù)庫出現(xiàn)臟數(shù)據(jù)。

2.再比如,你拿到這個消息做redis的set的操作,那就容易了,不用解決,因為你無論set幾次結(jié)果都是一樣的,set操作本來就算冪等操作。

3.如果上面兩種情況還不行,上大招。準(zhǔn)備一個第三方介質(zhì),來做消費記錄。以redis為例,給消息分配一個全局id,只要消費過該消息,將<id,message>以K-V形式寫入redis。那消費者開始消費前,先去redis中查詢有沒消費記錄即可。

13. 如何解決丟數(shù)據(jù)的問題?

1.生產(chǎn)者丟數(shù)據(jù)

生產(chǎn)者的消息沒有投遞到MQ中怎么辦?從生產(chǎn)者弄丟數(shù)據(jù)這個角度來看,RabbitMQ提供transaction和confirm模式來確保生產(chǎn)者不丟消息。

transaction機制就是說,發(fā)送消息前,開啟事物(channel.txSelect()),然后發(fā)送消息,如果發(fā)送過程中出現(xiàn)什么異常,事物就會回滾(channel.txRollback()),如果發(fā)送成功則提交事物(channel.txCommit())。

然而缺點就是吞吐量下降了。因此,按照博主的經(jīng)驗,生產(chǎn)上用confirm模式的居多。一旦channel進(jìn)入confirm模式,所有在該信道上面發(fā)布的消息都將會被指派一個唯一的ID(從1開始),一旦消息被投遞到所有匹配的隊列之后,rabbitMQ就會發(fā)送一個Ack給生產(chǎn)者(包含消息的唯一ID),這就使得生產(chǎn)者知道消息已經(jīng)正確到達(dá)目的隊列了.如果rabiitMQ沒能處理該消息,則會發(fā)送一個Nack消息給你,你可以進(jìn)行重試操作。

2.消息隊列丟數(shù)據(jù)

處理消息隊列丟數(shù)據(jù)的情況,一般是開啟持久化磁盤的配置。這個持久化配置可以和confirm機制配合使用,你可以在消息持久化磁盤后,再給生產(chǎn)者發(fā)送一個Ack信號。這樣,如果消息持久化磁盤之前,rabbitMQ陣亡了,那么生產(chǎn)者收不到Ack信號,生產(chǎn)者會自動重發(fā)。

那么如何持久化呢,這里順便說一下吧,其實也很容易,就下面兩步

①、將queue的持久化標(biāo)識durable設(shè)置為true,則代表是一個持久的隊列

②、發(fā)送消息的時候?qū)eliveryMode=2

這樣設(shè)置以后,rabbitMQ就算掛了,重啟后也能恢復(fù)數(shù)據(jù)。在消息還沒有持久化到硬盤時,可能服務(wù)已經(jīng)死掉,這種情況可以通過引入mirrored-queue即鏡像隊列,但也不能保證消息百分百不丟失(整個集群都掛掉)

3.消費者丟數(shù)據(jù)

啟用手動確認(rèn)模式可以解決這個問題

①自動確認(rèn)模式,消費者掛掉,待ack的消息回歸到隊列中。消費者拋出異常,消息會不斷的被重發(fā),直到處理成功。不會丟失消息,即便服務(wù)掛掉,沒有處理完成的消息會重回隊列,但是異常會讓消息不斷重試。

②手動確認(rèn)模式,如果消費者來不及處理就死掉時,沒有響應(yīng)ack時會重復(fù)發(fā)送一條信息給其他消費者;如果監(jiān)聽程序處理異常了,且未對異常進(jìn)行捕獲,會一直重復(fù)接收消息,然后一直拋異常;如果對異常進(jìn)行了捕獲,但是沒有在finally里ack,也會一直重復(fù)發(fā)送消息(重試機制)。

③不確認(rèn)模式,acknowledge="none" 不使用確認(rèn)機制,只要消息發(fā)送完成會立即在隊列移除,無論客戶端異常還是斷開,只要發(fā)送完就移除,不會重發(fā)。

14. 死信隊列和延遲隊列的使用?

死信消息:

消息被拒絕(Basic.Reject或Basic.Nack)并且設(shè)置 requeue 參數(shù)的值為 false
消息過期了
隊列達(dá)到最大的長度

過期消息:

在 rabbitmq 中存在2種方可設(shè)置消息的過期時間,第一種通過對隊列進(jìn)行設(shè)置,這種設(shè)置后,該隊列中所有的消息都存在相同的過期時間,第二種通過對消息本身進(jìn)行設(shè)置,那么每條消息的過期時間都不一樣。如果同時使用這2種方法,那么以過期時間小的那個數(shù)值為準(zhǔn)。當(dāng)消息達(dá)到過期時間還沒有被消費,那么那個消息就成為了一個 死信 消息。

隊列設(shè)置:在隊列申明的時候使用 x-message-ttl 參數(shù),單位為 毫秒

單個消息設(shè)置:是設(shè)置消息屬性的 expiration 參數(shù)的值,單位為 毫秒

延時隊列:在rabbitmq中不存在延時隊列,但是我們可以通過設(shè)置消息的過期時間和死信隊列來模擬出延時隊列。消費者監(jiān)聽死信交換器綁定的隊列,而不要監(jiān)聽消息發(fā)送的隊列。


有了以上的基礎(chǔ)知識,我們完成以下需求:

需求:用戶在系統(tǒng)中創(chuàng)建一個訂單,如果超過時間用戶沒有進(jìn)行支付,那么自動取消訂單。

分析:

1、上面這個情況,我們就適合使用延時隊列來實現(xiàn),那么延時隊列如何創(chuàng)建
2、延時隊列可以由 過期消息+死信隊列 來時間
3、過期消息通過隊列中設(shè)置 x-message-ttl 參數(shù)實現(xiàn)
4、死信隊列通過在隊列申明時,給隊列設(shè)置 x-dead-letter-exchange 參數(shù),然后另外申明一個隊列綁定x-dead-letter-exchange對應(yīng)的交換器。

    ConnectionFactory factory = new ConnectionFactory(); 
    factory.setHost("127.0.0.1"); 
    factory.setPort(AMQP.PROTOCOL.PORT); 
    factory.setUsername("guest"); 
    factory.setPassword("guest"); 
    Connection connection = factory.newConnection(); 
    Channel channel = connection.createChannel();
     
    // 聲明一個接收被刪除的消息的交換機和隊列 
    String EXCHANGE_DEAD_NAME = "exchange.dead"; 
    String QUEUE_DEAD_NAME = "queue_dead"; 
    channel.exchangeDeclare(EXCHANGE_DEAD_NAME, BuiltinExchangeType.DIRECT); 
    channel.queueDeclare(QUEUE_DEAD_NAME, false, false, false, null); 
    channel.queueBind(QUEUE_DEAD_NAME, EXCHANGE_DEAD_NAME, "routingkey.dead"); 
     
    String EXCHANGE_NAME = "exchange.fanout"; 
    String QUEUE_NAME = "queue_name"; 
    channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT); 
    Map<String, Object> arguments = new HashMap<String, Object>(); 
    // 統(tǒng)一設(shè)置隊列中的所有消息的過期時間 
    arguments.put("x-message-ttl", 30000); 
    // 設(shè)置超過多少毫秒沒有消費者來訪問隊列,就刪除隊列的時間 
    arguments.put("x-expires", 20000); 
    // 設(shè)置隊列的最新的N條消息,如果超過N條,前面的消息將從隊列中移除掉 
    arguments.put("x-max-length", 4); 
    // 設(shè)置隊列的內(nèi)容的最大空間,超過該閾值就刪除之前的消息
    arguments.put("x-max-length-bytes", 1024); 
    // 將刪除的消息推送到指定的交換機,一般x-dead-letter-exchange和x-dead-letter-routing-key需要同時設(shè)置
    arguments.put("x-dead-letter-exchange", "exchange.dead"); 
    // 將刪除的消息推送到指定的交換機對應(yīng)的路由鍵 
    arguments.put("x-dead-letter-routing-key", "routingkey.dead"); 
    // 設(shè)置消息的優(yōu)先級,優(yōu)先級大的優(yōu)先被消費 
    arguments.put("x-max-priority", 10); 
    channel.queueDeclare(QUEUE_NAME, false, false, false, arguments); 
    channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ""); 
    String message = "Hello RabbitMQ: "; 
     
    for(int i = 1; i <= 5; i++) { 
        // expiration: 設(shè)置單條消息的過期時間 
        AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties().builder()
                .priority(i).expiration( i * 1000 + ""); 
        channel.basicPublish(EXCHANGE_NAME, "", properties.build(), (message + i).getBytes("UTF-8")); 
    } 
    channel.close(); 
    connection.close();

15. 使用了消息隊列會有什么缺點?

1.系統(tǒng)可用性降低:你想啊,本來其他系統(tǒng)只要運行好好的,那你的系統(tǒng)就是正常的?,F(xiàn)在你非要加個消息隊列進(jìn)去,那消息隊列掛了,你的系統(tǒng)不是呵呵了。因此,系統(tǒng)可用性降低

2.系統(tǒng)復(fù)雜性增加:要多考慮很多方面的問題,比如一致性問題、如何保證消息不被重復(fù)消費,如何保證保證消息可靠傳輸。因此,需要考慮的東西更多,系統(tǒng)復(fù)雜性增大。

16. 消息隊列的作用與使用場景

異步:批量數(shù)據(jù)異步處理(批量上傳文件)
削峰:高負(fù)載任務(wù)負(fù)載均衡(電商秒殺搶購)
解耦:串行任務(wù)并行化(退貨流程解耦)
廣播:基于Pub/Sub實現(xiàn)一對多通信

17. 多個消費者監(jiān)聽一個隊列時,消息如何分發(fā)?

  • 輪詢: 默認(rèn)的策略,消費者輪流,平均地接收消息
  • 公平分發(fā): 根據(jù)消費者的能力來分發(fā)消息,給空閑的消費者發(fā)送更多消息

//當(dāng)消費者有x條消息沒有響應(yīng)ACK時,不再給這個消費者發(fā)送消息


channel.basicQos(int x)

18. 無法被路由的消息去了哪里?

無設(shè)置的情況下,無法路由(Routing key錯誤)的消息會被直接丟棄
解決方案:
mandatory設(shè)置為true,并配合ReturnListener,實現(xiàn)消息的回發(fā)

聲明交換機時,指定備份的交換機

 Map<String,Object> arguments = new HashMap<String,Object>();
    arguments.put("alternate-exchange","備份交換機名");

19. 消息在什么時候會變成死信?

  • 消息拒絕并且沒有設(shè)置重新入隊
  • 消息過期
  • 消息堆積,并且隊列達(dá)到最大長度,先入隊的消息會變成DL

20. RabbitMQ如何實現(xiàn)延時隊列?

利用TTL(隊列的消息存活時間或者消息存活時間),加上死信交換機

 // 設(shè)置屬性,消息10秒鐘過期
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
        .expiration("10000") // TTL

 // 指定隊列的死信交換機
Map<String,Object> arguments = new HashMap<String,Object>();
arguments.put("x-dead-letter-exchange","DLX_EXCHANGE");

21. 如何保證消息的可靠性投遞

發(fā)送方確認(rèn)模式:
將信道設(shè)置成confirm模式(發(fā)送方確認(rèn)模式),則所有在信道上發(fā)布的消息都會被指派一個唯一的ID。
一旦消息被投遞到目的隊列后,或者消息被寫入磁盤后(可持久化的消息),信道會發(fā)送一個確認(rèn)給生產(chǎn)者(包含消息唯一ID)。
如果RabbitMQ發(fā)生內(nèi)部錯誤從而導(dǎo)致消息丟失,會發(fā)送一條nack(not acknowledged,未確認(rèn))消息。
發(fā)送方確認(rèn)模式是異步的,生產(chǎn)者應(yīng)用程序在等待確認(rèn)的同時,可以繼續(xù)發(fā)送消息。當(dāng)確認(rèn)消息到達(dá)生產(chǎn)者應(yīng)用程序,生產(chǎn)者應(yīng)用程序的回調(diào)方法就會被觸發(fā)來處理確認(rèn)消息。

接收方確認(rèn)機制
接收方消息確認(rèn)機制:消費者接收每一條消息后都必須進(jìn)行確認(rèn)(消息接收和消息確認(rèn)是兩個不同操作)。只有消費者確認(rèn)了消息,RabbitMQ才能安全地把消息從隊列中刪除。
這里并沒有用到超時機制,RabbitMQ僅通過Consumer的連接中斷來確認(rèn)是否需要重新發(fā)送消息。也就是說,只要連接不中斷,RabbitMQ給了Consumer足夠長的時間來處理消息。保證數(shù)據(jù)的最終一致性;
下面羅列幾種特殊情況:
如果消費者接收到消息,在確認(rèn)之前斷開了連接或取消訂閱,RabbitMQ會認(rèn)為消息沒有被分發(fā),然后重新分發(fā)給下一個訂閱的消費者。(可能存在消息重復(fù)消費的隱患,需要去重)
如果消費者接收到消息卻沒有確認(rèn)消息,連接也未斷開,則RabbitMQ認(rèn)為該消費者繁忙,將不會給該消費者分發(fā)更多的消息。

22. 消息冪等性

生產(chǎn)者方面:可以對每條消息生成一個msgID,以控制消息重復(fù)投遞

 AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
    porperties.messageId(String.valueOF(UUID.randomUUID()))

消費者方面:消息體中必須攜帶一個業(yè)務(wù)ID,如銀行流水號,消費者可以根據(jù)業(yè)務(wù)ID去重,避免重復(fù)消費

23. 消息如何被優(yōu)先消費

//生產(chǎn)者
 Map<String, Object> argss = new HashMap<String, Object>();
        argss.put("x-max-priority",10);

//消費者
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .priority(5) // 優(yōu)先級,默認(rèn)為5,配合隊列的 x-max-priority 屬性使用

24. 如何保證消息的順序性

一個隊列只有一個消費者的情況下才能保證順序,否則只能通過全局ID實現(xiàn)(每條消息都一個msgId,關(guān)聯(lián)的消息擁有一個parentMsgId??梢栽谙M端實現(xiàn)前一條消息未消費,不處理下一條消息;也可以在生產(chǎn)端實現(xiàn)前一條消息未處理完畢,不發(fā)布下一條消息)

25. RabbitMQ的集群模式和集群節(jié)點類型

普通模式:默認(rèn)模式,以兩個節(jié)點(rabbit01,rabbit02)為例來進(jìn)行說明,對于Queue來說,消息實體只存在于其中一個節(jié)點rabbit01(或者rabbit02),rabbit01和rabbit02兩個節(jié)點僅有相同的元數(shù)據(jù),即隊列結(jié)構(gòu)。當(dāng)消息進(jìn)入rabbit01節(jié)點的Queue后,consumer從rabbit02節(jié)點消費時,RabbitMQ會臨時在rabbit01,rabbit02間進(jìn)行消息傳輸,把A中的消息實體取出并經(jīng)過B發(fā)送給consumer,所以consumer應(yīng)盡量連接每一個節(jié)點,從中取消息。即對于同一個邏輯隊列,要在多個節(jié)點建立物理Queue。否則無論consumer連rabbit01或rabbit02,出口總在rabbit01,會產(chǎn)生瓶頸。當(dāng)rabbit01節(jié)點故障后,rabbit02節(jié)點無法取到rabbit01節(jié)點中還未消費的消息實體。如果做了消息持久化,那么等到rabbit01節(jié)點恢復(fù),然后才可被消費。如果沒有消息持久化,就會產(chǎn)生消息丟失的現(xiàn)象。

鏡像模式:把需要的隊列做成鏡像隊列,存在與多個節(jié)點屬于RabibitMQ的HA方案,該模式解決了普通模式中的問題,其實質(zhì)和普通模式不同之處在于,消息體會主動在鏡像節(jié)點間同步,而不是在客戶端取數(shù)據(jù)時臨時拉取,該模式帶來的副作用也很明顯,除了降低系統(tǒng)性能外,如果鏡像隊列數(shù)量過多,加之大量的消息進(jìn)入,集群內(nèi)部的網(wǎng)絡(luò)帶寬將會被這種同步通訊大大消耗掉,所以在對可靠性要求比較高的場合中適用

節(jié)點分為內(nèi)存節(jié)點(保存狀態(tài)到內(nèi)存,但持久化的隊列和消息還是會保存到磁盤),磁盤節(jié)點(保存狀態(tài)到內(nèi)存和磁盤),一個集群中至少需要一個磁盤節(jié)點

26.如何自動刪除長時間沒有消費的消息

// 通過隊列屬性設(shè)置消息過期時間
        Map<String, Object> argss = new HashMap<String, Object>();
        argss.put("x-message-ttl",6000);

 // 對每條消息設(shè)置過期時間
        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .expiration("10000") // TTL

27.消息基于什么傳輸

RabbitMQ使用信道的方式來傳輸數(shù)據(jù)。信道是建立在真實的TCP連接內(nèi)的虛擬連接,且每條TCP連接上的信道數(shù)量沒有限制

28.如何確保消息不丟失

消息持久化,當(dāng)然前提是隊列必須持久化
RabbitMQ確保持久性消息能從服務(wù)器重啟中恢復(fù)的方式是,將它們寫入磁盤上的一個持久化日志文件,當(dāng)發(fā)布一條持久性消息到持久交換器上時,Rabbit會在消息提交到日志文件后才發(fā)送響應(yīng)。
一旦消費者從持久隊列中消費了一條持久化消息,RabbitMQ會在持久化日志中把這條消息標(biāo)記為等待垃圾收集。如果持久化消息在被消費之前RabbitMQ重啟,那么Rabbit會自動重建交換器和隊列(以及綁定),并重新發(fā)布持久化日志文件中的消息到合適的隊列。

RabbitMQ思維導(dǎo)圖

RabbitMQ知識集錦

更多思維導(dǎo)圖,關(guān)注公眾號下載~

推薦

Redis面試題集錦(精選)

Spring面試題集錦(精選)

SpringMVC面試題集錦(精選)

ProcessOn是一個在線作圖工具的聚合平臺~

文末

歡迎關(guān)注個人微信公眾號:Coder編程
歡迎關(guān)注Coder編程公眾號,主要分享數(shù)據(jù)結(jié)構(gòu)與算法、Java相關(guān)知識體系、框架知識及原理、Spring全家桶、微服務(wù)項目實戰(zhàn)、DevOps實踐之路、每日一篇互聯(lián)網(wǎng)大廠面試或筆試題以及PMP項目管理知識等。更多精彩內(nèi)容正在路上~

文章收錄至
Github: https://github.com/CoderMerlin/coder-programming
Gitee: https://gitee.com/573059382/coder-programming
歡迎關(guān)注并star~

微信公眾號

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容