訊息中介軟體MQ與RabbitMQ面試題(2020最新版)

2020-08-07 13:09:50

 

 

文章目錄

 

Java面試總結匯總,整理了包括Java基礎知識,集合容器,併發程式設計,JVM,常用開源框架Spring,MyBatis,數據庫,中介軟體等,包含了作爲一個Java工程師在面試中需要用到或者可能用到的絕大部分知識。歡迎大家閱讀,本人見識有限,寫的部落格難免有錯誤或者疏忽的地方,還望各位大佬指點,在此表示感激不盡。文章持續更新中…

序號 內容 鏈接地址
1 Java基礎知識面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390612
2 Java集合容器面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588551
3 Java異常面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390689
4 併發程式設計面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104863992
5 JVM面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390752
6 Spring面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397516
7 Spring MVC面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397427
8 Spring Boot面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397299
9 Spring Cloud面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397367
10 MyBatis面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/101292950
11 Redis面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/103522351
12 MySQL數據庫面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104778621
13 訊息中介軟體MQ與RabbitMQ面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588612
14 Dubbo面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104390006
15 Linux面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104588679
16 Tomcat面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397665
17 ZooKeeper面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104397719
18 Netty面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/104391081
19 架構設計&分佈式&數據結構與演算法面試題(2020最新版) https://thinkwon.blog.csdn.net/article/details/105870730

爲什麼使用MQ?MQ的優點

簡答

  • 非同步處理 - 相比於傳統的序列、並行方式,提高了系統吞吐量。
  • 應用解耦 - 系統間通過訊息通訊,不用關心其他系統的處理。
  • 流量削鋒 - 可以通過訊息佇列長度控制請求量;可以緩解短時間內的高併發請求。
  • 日誌處理 - 解決大量日誌傳輸。
  • 訊息通訊 - 訊息佇列一般都內建了高效的通訊機制 機製,因此也可以用在純的訊息通訊。比如實現對等訊息佇列,或者聊天室等。

詳答

主要是:解耦、非同步、削峯。

解耦:A 系統發送數據到 BCD 三個系統,通過介面呼叫發送。如果 E 系統也要這個數據呢?那如果 C 系統現在不需要了呢?A 系統負責人幾乎崩潰…A 系統跟其它各種亂七八糟的系統嚴重耦合,A 系統產生一條比較關鍵的數據,很多系統都需要 A 系統將這個數據發送過來。如果使用 MQ,A 系統產生一條數據,發送到 MQ 裏面去,哪個系統需要數據自己去 MQ 裏面消費。如果新系統需要數據,直接從 MQ 裡消費即可;如果某個系統不需要這條數據了,就取消對 MQ 訊息的消費即可。這樣下來,A 系統壓根兒不需要去考慮要給誰發送數據,不需要維護這個程式碼,也不需要考慮人家是否呼叫成功、失敗超時等情況。

就是一個系統或者一個模組,呼叫了多個系統或者模組,互相之間的呼叫很複雜,維護起來很麻煩。但是其實這個呼叫是不需要直接同步呼叫介面的,如果用 MQ 給它非同步化解耦。

非同步:A 系統接收一個請求,需要在自己本地寫庫,還需要在 BCD 三個系統寫庫,自己本地寫庫要 3ms,BCD 三個系統分別寫庫要 300ms、450ms、200ms。最終請求總延時是 3 + 300 + 450 + 200 = 953ms,接近 1s,使用者感覺搞個什麼東西,慢死了慢死了。使用者通過瀏覽器發起請求。如果使用 MQ,那麼 A 系統連續發送 3 條訊息到 MQ 佇列中,假如耗時 5ms,A 系統從接受一個請求到返迴響應給使用者,總時長是 3 + 5 = 8ms。

削峯:減少高峯時期對伺服器壓力。

訊息佇列有什麼優缺點?RabbitMQ有什麼優缺點?

優點上面已經說了,就是在特殊場景下有其對應的好處解耦非同步削峯

缺點有以下幾個:

系統可用性降低

本來系統執行好好的,現在你非要加入個訊息佇列進去,那訊息佇列掛了,你的系統不是呵呵了。因此,系統可用性會降低;

系統複雜度提高

加入了訊息佇列,要多考慮很多方面的問題,比如:一致性問題、如何保證訊息不被重複消費、如何保證訊息可靠性傳輸等。因此,需要考慮的東西更多,複雜性增大。

一致性問題

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

所以訊息佇列實際是一種非常複雜的架構,你引入它有很多好處,但是也得針對它帶來的壞處做各種額外的技術方案和架構來規避掉,做好之後,你會發現,媽呀,系統複雜度提升了一個數量級,也許是複雜了 10 倍。但是關鍵時刻,用,還是得用的。

你們公司生產環境用的是什麼訊息中介軟體?

這個首先你可以說下你們公司選用的是什麼訊息中介軟體,比如用的是RabbitMQ,然後可以初步給一些你對不同MQ中介軟體技術的選型分析。

舉個例子:比如說ActiveMQ是老牌的訊息中介軟體,國內很多公司過去運用的還是非常廣泛的,功能很強大。

但是問題在於沒法確認ActiveMQ可以支撐網際網路公司的高併發、高負載以及高吞吐的複雜場景,在國內網際網路公司落地較少。而且使用較多的是一些傳統企業,用ActiveMQ做非同步呼叫和系統解耦。

然後你可以說說RabbitMQ,他的好處在於可以支撐高併發、高吞吐、效能很高,同時有非常完善便捷的後臺管理介面可以使用。

另外,他還支援叢集化、高可用部署架構、訊息高可靠支援,功能較爲完善。

而且經過調研,國內各大網際網路公司落地大規模RabbitMQ叢集支撐自身業務的case較多,國內各種中小型網際網路公司使用RabbitMQ的實踐也比較多。

除此之外,RabbitMQ的開源社羣很活躍,較高頻率的迭代版本,來修復發現的bug以及進行各種優化,因此綜合考慮過後,公司採取了RabbitMQ。

但是RabbitMQ也有一點缺陷,就是他自身是基於erlang語言開發的,所以導致較爲難以分析裏面的原始碼,也較難進行深層次的原始碼定製和改造,畢竟需要較爲紮實的erlang語言功底纔可以。

然後可以聊聊RocketMQ,是阿裡開源的,經過阿裡的生產環境的超高併發、高吞吐的考驗,效能卓越,同時還支援分佈式事務等特殊場景。

而且RocketMQ是基於Java語言開發的,適合深入閱讀原始碼,有需要可以站在原始碼層面解決線上生產問題,包括原始碼的二次開發和改造。

另外就是Kafka。Kafka提供的訊息中介軟體的功能明顯較少一些,相對上述幾款MQ中介軟體要少很多。

但是Kafka的優勢在於專爲超高吞吐量的實時日誌採集、實時數據同步、實時數據計算等場景來設計。

因此Kafka在大數據領域中配合實時計算技術(比如Spark Streaming、Storm、Flink)使用的較多。但是在傳統的MQ中介軟體使用場景中較少採用。

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

  ActiveMQ RabbitMQ RocketMQ Kafka ZeroMQ
單機吞吐量 比RabbitMQ低 2.6w/s(訊息做持久化) 11.6w/s 17.3w/s 29w/s
開發語言 Java Erlang Java Scala/Java C
主要維護者 Apache Mozilla/Spring Alibaba Apache iMatix,創始人已去世
成熟度 成熟 成熟 開源版本不夠成熟 比較成熟 只有C、PHP等版本成熟
訂閱形式 對等(p2p)、廣播(發佈-訂閱) 提供了4種:direct, topic ,Headers和fanout。fanout就是廣播模式 基於topic/messageTag以及按照訊息型別、屬性進行正則匹配的發佈訂閱模式 基於topic以及按照topic進行正則匹配的發佈訂閱模式 對等(p2p)
持久化 支援少量堆積 支援少量堆積 支援大量堆積 支援大量堆積 不支援
順序訊息 不支援 不支援 支援 支援 不支援
效能穩定性 一般 較差 很好
叢集方式 支援簡單叢集模式,比如’主-備’,對高階叢集模式支援不好。 支援簡單叢集,'複製’模式,對高階叢集模式支援不好。 常用 多對’Master-Slave’ 模式,開源版本需手動切換Slave變成Master 天然的‘Leader-Slave’無狀態叢集,每台伺服器既是Master也是Slave 不支援
管理介面 一般 較好 一般

綜上,各種對比之後,有如下建議:

一般的業務系統要引入 MQ,最早大家都用 ActiveMQ,但是現在確實大家用的不多了,沒經過大規模吞吐量場景的驗證,社羣也不是很活躍,所以大家還是算了吧,我個人不推薦用這個了;

後來大家開始用 RabbitMQ,但是確實 erlang 語言阻止了大量的 Java 工程師去深入研究和掌控它,對公司而言,幾乎處於不可控的狀態,但是確實人家是開源的,比較穩定的支援,活躍度也高;

不過現在確實越來越多的公司會去用 RocketMQ,確實很不錯,畢竟是阿裡出品,但社羣可能有突然黃掉的風險(目前 RocketMQ 已捐給 Apache,但 GitHub 上的活躍度其實不算高)對自己公司技術實力有絕對自信的,推薦用 RocketMQ,否則回去老老實實用 RabbitMQ 吧,人家有活躍的開源社羣,絕對不會黃。

所以中小型公司,技術實力較爲一般,技術挑戰不是特別高,用 RabbitMQ 是不錯的選擇;大型公司,基礎架構研發實力較強,用 RocketMQ 是很好的選擇。

如果是大數據領域的實時計算、日誌採集等場景,用 Kafka 是業內標準的,絕對沒問題,社羣活躍度很高,絕對不會黃,何況幾乎是全世界這個領域的事實性規範。

MQ 有哪些常見問題?如何解決這些問題?

MQ 的常見問題有:

  1. 訊息的順序問題
  2. 訊息的重複問題

訊息的順序問題

訊息有序指的是可以按照訊息的發送順序來消費。

假如生產者產生了 2 條訊息:M1、M2,假定 M1 發送到 S1,M2 發送到 S2,如果要保證 M1 先於 M2 被消費,怎麼做?

img

解決方案:

(1)保證生產者 - MQServer - 消費者是一對一對一的關係

img

缺陷:

  • 並行度就會成爲訊息系統的瓶頸(吞吐量不夠)
  • 更多的例外處理,比如:只要消費端出現問題,就會導致整個處理流程阻塞,我們不得不花費更多的精力來解決阻塞的問題。 (2)通過合理的設計或者將問題分解來規避。
  • 不關注亂序的應用實際大量存在
  • 佇列無序並不意味着訊息無序 所以從業務層面來保證訊息的順序而不僅僅是依賴於訊息系統,是一種更合理的方式。

訊息的重複問題

造成訊息重複的根本原因是:網路不可達。

所以解決這個問題的辦法就是繞過這個問題。那麼問題就變成了:如果消費端收到兩條一樣的訊息,應該怎樣處理?

消費端處理訊息的業務邏輯保持冪等性。只要保持冪等性,不管來多少條重複訊息,最後處理的結果都一樣。保證每條訊息都有唯一編號且保證訊息處理成功與去重表的日誌同時出現。利用一張日誌表來記錄已經處理成功的訊息的 ID,如果新到的訊息 ID 已經在日誌表中,那麼就不再處理這條訊息。

什麼是RabbitMQ?

RabbitMQ是一款開源的,Erlang編寫的,基於AMQP協定的訊息中介軟體

rabbitmq 的使用場景

(1)服務間非同步通訊

(2)順序消費

(3)定時任務

(4)請求削峯

RabbitMQ基本概念

  • Broker: 簡單來說就是訊息佇列伺服器實體
  • Exchange: 訊息交換機,它指定訊息按什麼規則,路由到哪個佇列
  • Queue: 訊息佇列載體,每個訊息都會被投入到一個或多個佇列
  • Binding: 系結,它的作用就是把exchange和queue按照路由規則系結起來
  • Routing Key: 路由關鍵字,exchange根據這個關鍵字進行訊息投遞
  • VHost: vhost 可以理解爲虛擬 broker ,即 mini-RabbitMQ server。其內部均含有獨立的 queue、exchange 和 binding 等,但最最重要的是,其擁有獨立的許可權系統,可以做到 vhost 範圍的使用者控制。當然,從 RabbitMQ 的全域性角度,vhost 可以作爲不同許可權隔離的手段(一個典型的例子就是不同的應用可以跑在不同的 vhost 中)。
  • Producer: 訊息生產者,就是投遞訊息的程式
  • Consumer: 訊息消費者,就是接受訊息的程式
  • Channel: 訊息通道,在用戶端的每個連線裡,可建立多個channel,每個channel代表一個對談任務

由Exchange、Queue、RoutingKey三個才能 纔能決定一個從Exchange到Queue的唯一的線路。

RabbitMQ的工作模式

一.simple模式(即最簡單的收發模式)

img

1.訊息產生訊息,將訊息放入佇列

2.訊息的消費者(consumer) 監聽 訊息佇列,如果佇列中有訊息,就消費掉,訊息被拿走後,自動從佇列中刪除(隱患 訊息可能沒有被消費者正確處理,已經從佇列中消失了,造成訊息的丟失,這裏可以設定成手動的ack,但如果設定成手動ack,處理完後要及時發送ack訊息給佇列,否則會造成記憶體溢位)。

二.work工作模式(資源的競爭)

img

1.訊息產生者將訊息放入佇列消費者可以有多個,消費者1,消費者2同時監聽同一個佇列,訊息被消費。C1 C2共同爭搶當前的訊息佇列內容,誰先拿到誰負責消費訊息(隱患:高併發情況下,預設會產生某一個訊息被多個消費者共同使用,可以設定一個開關(syncronize) 保證一條訊息只能被一個消費者使用)。

三.publish/subscribe發佈訂閱(共用資源)

img

1、每個消費者監聽自己的佇列;

2、生產者將訊息發給broker,由交換機將訊息轉發到系結此交換機的每個佇列,每個系結交換機的佇列都將接收到訊息。

四.routing路由模式

img

1.訊息生產者將訊息發送給交換機按照路由判斷,路由是字串(info) 當前產生的訊息攜帶路由字元(物件的方法),交換機根據路由的key,只能匹配上路由key對應的訊息佇列,對應的消費者才能 纔能消費訊息;

2.根據業務功能定義路由字串

3.從系統的程式碼邏輯中獲取對應的功能字串,將訊息任務扔到對應的佇列中。

4.業務場景:error 通知;EXCEPTION;錯誤通知的功能;傳統意義的錯誤通知;客戶通知;利用key路由,可以將程式中的錯誤封裝成訊息傳入到訊息佇列中,開發者可以自定義消費者,實時接收錯誤;

五.topic 主題模式(路由模式的一種)

img

1.星號井號代表萬用字元

2.星號代表多個單詞,井號代表一個單詞

3.路由功能新增模糊匹配

4.訊息產生者產生訊息,把訊息交給交換機

5.交換機根據key的規則模糊匹配到對應的佇列,由佇列的監聽消費者接收訊息消費

(在我的理解看來就是routing查詢的一種模糊匹配,就類似sql的模糊查詢方式)

如何保證RabbitMQ訊息的順序性?

拆分多個 queue,每個 queue 一個 consumer,就是多一些 queue 而已,確實是麻煩點;或者就一個 queue 但是對應一個 consumer,然後這個 consumer 內部用記憶體佇列做排隊,然後分發給底層不同的 worker 來處理。

訊息如何分發?

若該佇列至少有一個消費者訂閱,訊息將以回圈(round-robin)的方式發送給消費者。每條訊息只會分發給一個訂閱的消費者(前提是消費者能夠正常處理訊息並進行確認)。通過路由可實現多消費的功能

訊息怎麼路由?

訊息提供方->路由->一至多個佇列訊息發佈到交換器時,訊息將擁有一個路由鍵(routing key),在訊息建立時設定。通過佇列路由鍵,可以把佇列系結到交換器上。訊息到達交換器後,RabbitMQ 會將訊息的路由鍵與佇列的路由鍵進行匹配(針對不同的交換器有不同的路由規則);

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

fanout:如果交換器收到訊息,將會廣播到所有系結的佇列上

direct:如果路由鍵完全匹配,訊息就被投遞到相應的佇列

topic:可以使來自不同源頭的訊息能夠到達同一個佇列。 使用 topic 交換器時,可以使用萬用字元

訊息基於什麼傳輸?

由於 TCP 連線的建立和銷燬開銷較大,且併發數受系統資源限制,會造成效能瓶頸。RabbitMQ 使用通道的方式來傳輸數據。通道是建立在真實的 TCP 連線內的虛擬連線,且每條 TCP 連線上的通道數量沒有限制。

如何保證訊息不被重複消費?或者說,如何保證訊息消費時的冪等性?

先說爲什麼會重複消費:正常情況下,消費者在消費訊息的時候,消費完畢後,會發送一個確認訊息給訊息佇列,訊息佇列就知道該訊息被消費了,就會將該訊息從訊息佇列中刪除;

但是因爲網路傳輸等等故障,確認資訊沒有傳送到訊息佇列,導致訊息佇列不知道自己已經消費過該訊息了,再次將訊息分發給其他的消費者。

針對以上問題,一個解決思路是:保證訊息的唯一性,就算是多次傳輸,不要讓訊息的多次消費帶來影響;保證訊息等冪性;

比如:在寫入訊息佇列的數據做唯一標示,消費訊息時,根據唯一標識判斷是否消費過;

假設你有個系統,消費一條訊息就往數據庫裡插入一條數據,要是你一個訊息重複兩次,你不就插入了兩條,這數據不就錯了?但是你要是消費到第二次的時候,自己判斷一下是否已經消費過了,若是就直接扔了,這樣不就保留了一條數據,從而保證了數據的正確性。

如何確保訊息正確地發送至 RabbitMQ? 如何確保訊息接收方消費了訊息?

發送方確認模式

將通道設定成 confirm 模式(發送方確認模式),則所有在通道上發佈的訊息都會被指派一個唯一的 ID。

一旦訊息被投遞到目的佇列後,或者訊息被寫入磁碟後(可持久化的訊息),通道會發送一個確認給生產者(包含訊息唯一 ID)。

如果 RabbitMQ 發生內部錯誤從而導致訊息丟失,會發送一條 nack(notacknowledged,未確認)訊息。

發送方確認模式是非同步的,生產者應用程式在等待確認的同時,可以繼續發送訊息。當確認訊息到達生產者應用程式,生產者應用程式的回撥方法就會被觸發來處理確認訊息。

接收方確認機制 機製

消費者接收每一條訊息後都必須進行確認(訊息接收和訊息確認是兩個不同操作)。只有消費者確認了訊息,RabbitMQ 才能 纔能安全地把訊息從佇列中刪除。

這裏並沒有用到超時機制 機製,RabbitMQ 僅通過 Consumer 的連線中斷來確認是否需要重新發送訊息。也就是說,只要連線不中斷,RabbitMQ 給了 Consumer 足夠長的時間來處理訊息。保證數據的最終一致性;

下面 下麪羅列幾種特殊情況

  • 如果消費者接收到訊息,在確認之前斷開了連線或取消訂閱,RabbitMQ 會認爲訊息沒有被分發,然後重新分發給下一個訂閱的消費者。(可能存在訊息重複消費的隱患,需要去重)
  • 如果消費者接收到訊息卻沒有確認訊息,連線也未斷開,則 RabbitMQ 認爲該消費者繁忙,將不會給該消費者分發更多的訊息。

如何保證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信號,生產者會自動重發。

那麼如何持久化呢?

這裏順便說一下吧,其實也很容易,就下面 下麪兩步

  1. 將queue的持久化標識durable設定爲true,則代表是一個持久的佇列
  2. 發送訊息的時候將deliveryMode=2

這樣設定以後,即使rabbitMQ掛了,重新啓動後也能恢復數據

消費者丟失訊息:消費者丟數據一般是因爲採用了自動確認訊息模式,改爲手動確認訊息即可!

消費者在收到訊息之後,處理訊息之前,會自動回覆 回復RabbitMQ已收到訊息;

如果這時處理訊息失敗,就會丟失該訊息;

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

爲什麼不應該對所有的 message 都使用持久化機制 機製?

首先,必然導致效能的下降,因爲寫磁碟比寫 RAM 慢的多,message 的吞吐量可能有 10 倍的差距。

其次,message 的持久化機制 機製用在 RabbitMQ 的內建 cluster 方案時會出現「坑爹」問題。矛盾點在於,若 message 設定了 persistent 屬性,但 queue 未設定 durable 屬性,那麼當該 queue 的 owner node 出現異常後,在未重建該 queue 前,發往該 queue 的 message 將被 blackholed ;若 message 設定了 persistent 屬性,同時 queue 也設定了 durable 屬性,那麼當 queue 的 owner node 異常且無法重新啓動的情況下,則該 queue 無法在其他 node 上重建,只能等待其 owner node 重新啓動後,才能 纔能恢復該 queue 的使用,而在這段時間內發送給該 queue 的 message 將被 blackholed 。

所以,是否要對 message 進行持久化,需要綜合考慮效能需要,以及可能遇到的問題。若想達到 100,000 條/秒以上的訊息吞吐量(單 RabbitMQ 伺服器),則要麼使用其他的方式來確保 message 的可靠 delivery ,要麼使用非常快速的儲存系統以支援全持久化(例如使用 SSD)。另外一種處理原則是:僅對關鍵訊息作持久化處理(根據業務重要程度),且應該保證關鍵訊息的量不會導致效能瓶頸。

如何保證高可用的?RabbitMQ 的叢集

RabbitMQ 是比較有代表性的,因爲是基於主從(非分佈式)做高可用性的,我們就以 RabbitMQ 爲例子講解第一種 MQ 的高可用性怎麼實現。RabbitMQ 有三種模式:單機模式、普通叢集模式、映象叢集模式。

單機模式,就是 Demo 級別的,一般就是你本地啓動了玩玩兒的?,沒人生產用單機模式

普通叢集模式,意思就是在多臺機器上啓動多個 RabbitMQ 範例,每個機器啓動一個。你建立的 queue,只會放在一個 RabbitMQ 範例上,但是每個範例都同步 queue 的元數據(元數據可以認爲是 queue 的一些設定資訊,通過元數據,可以找到 queue 所在範例)。你消費的時候,實際上如果連線到了另外一個範例,那麼那個範例會從 queue 所在範例上拉取數據過來。這方案主要是提高吞吐量的,就是說讓叢集中多個節點來服務某個 queue 的讀寫操作。

映象叢集模式:這種模式,纔是所謂的 RabbitMQ 的高可用模式。跟普通叢集模式不一樣的是,在映象叢集模式下,你建立的 queue,無論元數據還是 queue 裡的訊息都會存在於多個範例上,就是說,每個 RabbitMQ 節點都有這個 queue 的一個完整映象,包含 queue 的全部數據的意思。然後每次你寫訊息到 queue 的時候,都會自動把訊息同步到多個範例的 queue 上。RabbitMQ 有很好的管理控制檯,就是在後臺新增一個策略,這個策略是映象叢集模式的策略,指定的時候是可以要求數據同步到所有節點的,也可以要求同步到指定數量的節點,再次建立 queue 的時候,應用這個策略,就會自動將數據同步到其他的節點上去了。這樣的話,好處在於,你任何一個機器宕機了,沒事兒,其它機器(節點)還包含了這個 queue 的完整數據,別的 consumer 都可以到其它節點上去消費數據。壞處在於,第一,這個效能開銷也太大了吧,訊息需要同步到所有機器上,導致網路頻寬壓力和消耗很重!RabbitMQ 一個 queue 的數據都是放在一個節點裏的,映象叢集下,也是每個節點都放這個 queue 的完整數據。

如何解決訊息佇列的延時以及過期失效問題?訊息佇列滿了以後該怎麼處理?有幾百萬訊息持續積壓幾小時,說說怎麼解決?

訊息積壓處理辦法:臨時緊急擴容:

先修復 consumer 的問題,確保其恢復消費速度,然後將現有 cnosumer 都停掉。
新建一個 topic,partition 是原來的 10 倍,臨時建立好原先 10 倍的 queue 數量。
然後寫一個臨時的分發數據的 consumer 程式,這個程式部署上去消費積壓的數據,消費之後不做耗時的處理,直接均勻輪詢寫入臨時建立好的 10 倍數量的 queue。
接着臨時徵用 10 倍的機器來部署 consumer,每一批 consumer 消費一個臨時 queue 的數據。這種做法相當於是臨時將 queue 資源和 consumer 資源擴大 10 倍,以正常的 10 倍速度來消費數據。
等快速消費完積壓數據之後,得恢復原先部署的架構,重新用原先的 consumer 機器來消費訊息。
MQ中訊息失效:假設你用的是 RabbitMQ,RabbtiMQ 是可以設定過期時間的,也就是 TTL。如果訊息在 queue 中積壓超過一定的時間就會被 RabbitMQ 給清理掉,這個數據就沒了。那這就是第二個坑了。這就不是說數據會大量積壓在 mq 裡,而是大量的數據會直接搞丟。我們可以採取一個方案,就是批次重導,這個我們之前線上也有類似的場景幹過。就是大量積壓的時候,我們當時就直接丟棄數據了,然後等過了高峯期以後,比如大家一起喝咖啡熬夜到晚上12點以後,使用者都睡覺了。這個時候我們就開始寫程式,將丟失的那批數據,寫個臨時程式,一點一點的查出來,然後重新灌入 mq 裏面去,把白天丟的數據給他補回來。也只能是這樣了。假設 1 萬個訂單積壓在 mq 裏面,沒有處理,其中 1000 個訂單都丟了,你只能手動寫程式把那 1000 個訂單給查出來,手動發到 mq 裡去再補一次。

mq訊息佇列塊滿了:如果訊息積壓在 mq 裡,你很長時間都沒有處理掉,此時導致 mq 都快寫滿了,咋辦?這個還有別的辦法嗎?沒有,誰讓你第一個方案執行的太慢了,你臨時寫程式,接入數據來消費,消費一個丟棄一個,都不要了,快速消費掉所有的訊息。然後走第二個方案,到了晚上再補數據吧。

設計MQ思路

比如說這個訊息佇列系統,我們從以下幾個角度來考慮一下:

首先這個 mq 得支援可伸縮性吧,就是需要的時候快速擴容,就可以增加吞吐量和容量,那怎麼搞?設計個分佈式的系統唄,參照一下 kafka 的設計理念,broker -> topic -> partition,每個 partition 放一個機器,就存一部分數據。如果現在資源不夠了,簡單啊,給 topic 增加 partition,然後做數據遷移,增加機器,不就可以存放更多數據,提供更高的吞吐量了?

其次你得考慮一下這個 mq 的數據要不要落地磁碟吧?那肯定要了,落磁碟才能 纔能保證別進程掛了數據就丟了。那落磁碟的時候怎麼落啊?順序寫,這樣就沒有磁碟隨機讀寫的定址開銷,磁碟順序讀寫的效能是很高的,這就是 kafka 的思路。

其次你考慮一下你的 mq 的可用性啊?這個事兒,具體參考之前可用性那個環節講解的 kafka 的高可用保障機制 機製。多副本 -> leader & follower -> broker 掛了重新選舉 leader 即可對外服務。

能不能支援數據 0 丟失啊?可以的,參考我們之前說的那個 kafka 數據零丟失方案。


---------------------
作者:ThinkWon
來源:CSDN
原文:https://blog.csdn.net/ThinkWon/article/details/104588612
版權宣告:本文爲作者原創文章,轉載請附上博文鏈接!
內容解析By:CSDN,CNBLOG部落格文章一鍵轉載外掛