題目
- 為什么使用消息隊列?
- 消息隊列有什么優點和缺點?
- Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么區別,以及適合哪些場景?
消息中間件各種面試題:
消息中間件面試題:消息丟失怎么辦?
消息中間件面試題:消息隊列的優缺點,區別
消息中間件面試題:消息中間件的高可用
消息中間件面試題:如何保證消息的順序性
消息中間件面試題:如何保證消息不被重復消費
消息中間件面試題:如何解決消息隊列的延時以及過期失效問題?消息隊列滿了以后該怎么處理?有幾百萬消息持續積壓幾小時呢?
消息中間件面試題:如果讓你寫一個消息隊列,該如何進行架構設計?
問題目錄見簡書轉載博客:https://www.jianshu.com/p/eaafb1581e55
什么是消息隊列
在正式介紹和對比Kafka、RabbitMQ、RocketMQ之前,我們來說說什么是消息隊列(Message queue,簡稱MQ),從字面理解就是一個保存消息的一個容器。那么我們為何需要這樣一個容器呢?其實就是為了解耦各個系統,我們來舉個例子:

有這么一個簡單的場景,系統A負責生成userID,並調用系統B、C。如果系統BC頻繁變化是否需要userID參數,則系統A的代碼就得不斷變化,如果哪天又來了系統DEF……也需要這個參數,則系統A又要加入很多業務邏輯,這樣子各他系統之間就容易產生相互影響,另外大量的系統與A發生交互也容易產生問題。

加了消息隊列后,A只負責產生userID,至於誰要用這個參數,怎么用?系統A不管。對這個數據感興趣的系統自己去取用即可,各個系統之間就實現了解耦。而且解耦后,整個服務業變成了一個異步的方式,系統A產生數據后,不用依次調用BCD來累計耗時,各系統可以同時來取用消息隊列的數據進行處理,加大吞吐。
消息隊列的特點
1、先進先出:消息隊列的順序在入隊的時候就基本已經確定了,一般是不需人工干預的。
2、發布訂閱:發布訂閱是一種很高效的處理方式,如果不發生阻塞,基本可以當成是同步操作。
3、持久化:持久化確保消息隊列的使用不只是一個部分場景的輔助工具,而是讓消息隊列能像數據庫一樣存儲核心的數據。
4、分布式:在現在大流量、大數據的使用場景下,支持分布式的部署,才能被廣泛使用。消息隊列的定位就是一個高性能的中間件。
Kafka、RabbitMQ、RocketMQ介紹
Kafka
Kafka是LinkedIn開源的分布式發布-訂閱消息系統,目前歸屬於Apache頂級項目。Kafka主要為高吞吐量的訂閱發布系統而設計,追求速度與持久化。kafka中的消息由鍵、值、時間戳組成,kafka不記錄每個消息被誰使用,只通過偏移量記錄哪些消息是未讀的,kafka中可以指定消費組來實現訂閱發布的功能。
Kafka是由Apache軟件基金會開發的一個開源流處理平台,由Scala和Java編寫。Kafka是一種高吞吐量的分布式發布訂閱消息系統,它可以處理消費者規模的網站中的所有動作流數據。 這種動作(網頁瀏覽,搜索和其他用戶的行動)是在現代網絡上的許多社會功能的一個關鍵因素。 這些數據通常是由於吞吐量的要求而通過處理日志和日志聚合來解決。 對於像Hadoop的一樣的日志數據和離線分析系統,但又要求實時處理的限制,這是一個可行的解決方案。Kafka的目的是通過Hadoop的並行加載機制來統一線上和離線的消息處理,也是為了通過集群來提供實時的消息。
特點:
- 通過O(1)的磁盤數據結構提供消息的持久化,這種結構對於即使數以TB的消息存儲也能夠保持長時間的穩定性能。
- 高吞吐量 :即使是非常普通的硬件Kafka也可以
- 支持每秒數百萬支持通過Kafka服務器和消費機集群來分區消息。
- 支持Hadoop並行數據加載的消息。
RabbitMQ
RabbitMQ是使用Erlang語言開發的開源消息隊列系統,基於AMQP協議來實現。AMQP的主要特征是面向消息、隊列、路由(包括點對點和發布/訂閱)、可靠性、安全。AMQP協議更多用在企業系統內,對數據一致性、穩定性和可靠性要求很高的場景,對性能和吞吐量的要求還在其次。
RabbitMQ是一個在AMQP基礎上完成的,可復用的企業消息系統。它遵循Mozilla Public License開源協議。
RabbitMQ 最初起源於金融系統,用於在分布式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。
具體特點包括:
- 可靠性(Reliability)RabbitMQ 使用一些機制來保證可靠性,如持久化、傳輸確認、發布確認。
- 靈活的路由(Flexible Routing)在消息進入隊列之前,通過 Exchange 來路由消息的。對於典型的路由功能,RabbitMQ 已經提供了一些內置的 Exchange 來實現。針對更復雜的路由功能,可以將多個 Exchange 綁定在一起,也通過插件機制實現自己的 Exchange 。
- 消息集群(Clustering)多個 RabbitMQ 服務器可以組成一個集群,形成一個邏輯 Broker 。
- 高可用(Highly Available Queues)隊列可以在集群中的機器上進行鏡像,使得在部分節點出問題的情況下隊列仍然可用。
- 多種協議(Multi-protocol)RabbitMQ 支持多種消息隊列協議,比如 STOMP、MQTT 等等。
- 多語言客戶端(Many Clients)RabbitMQ 幾乎支持所有常用語言,比如 Java、.NET、Ruby 等等。
- 管理界面(Management UI)RabbitMQ 提供了一個易用的用戶界面,使得用戶可以監控和管理消息 Broker 的許多方面。
- 跟蹤機制(Tracing)如果消息異常,RabbitMQ 提供了消息跟蹤機制,使用者可以找出發生了什么。
- 插件機制(Plugin System)RabbitMQ 提供了許多插件,來從多方面進行擴展,也可以編寫自己的插件。
RocketMQ
RocketMQ是阿里開源的消息中間件,它是純Java開發,具有高吞吐量、高可用性、適合大規模分布式系統應用的特點。RocketMQ思路起源於Kafka,但並不是Kafka的一個Copy,它對消息的可靠傳輸及事務性做了優化,目前在阿里集團被廣泛應用於交易、充值、流計算、消息推送、日志流式處理、binglog分發等場景。支持的客戶端語言不多,目前是Java及C++,其中C++還不成熟。
RocketMQ 是一款開源的分布式消息系統,基於高可用分布式集群技術,提供低延時的、高可靠的消息發布與訂閱服務。同時,廣泛應用於多個領域,包括異步通信解耦、企業解決方案、金融支付、電信、電子商務、快遞物流、廣告營銷、社交、即時通信、移動應用、手游、視頻、物聯網、車聯網等。
具有以下特點:
- 能夠保證嚴格的消息順序
- 提供豐富的消息拉取模式
- 高效的訂閱者水平擴展能力
- 實時的消息訂閱機制
- 億級消息堆積能力
- Metaq3.0 版本改名,產品名稱改為RocketMQ
ActiveMQ
ActiveMQ 是Apache出品,最流行的,能力強勁的開源消息總線。ActiveMQ 是一個完全支持JMS1.1和J2EE 1.4規范的 JMS Provider實現,盡管JMS規范出台已經是很久的事情了,但是JMS在當今的J2EE應用中間仍然扮演着特殊的地位。

ActiveMQ特性如下:
-
多種語言和協議編寫客戶端。語言: Java,C,C++,C#,Ruby,Perl,Python,PHP。應用協議: OpenWire,Stomp REST,WS Notification,XMPP,AMQP
-
完全支持JMS1.1和J2EE 1.4規范 (持久化,XA消息,事務)
-
對Spring的支持,ActiveMQ可以很容易內嵌到使用Spring的系統里面去,而且也支持Spring2.0的特性
-
通過了常見J2EE服務器(如 Geronimo,JBoss 4,GlassFish,WebLogic)的測試,其中通過JCA 1.5 resource adaptors的配置,可以讓ActiveMQ可以自動的部署到任何兼容J2EE 1.4 商業服務器上
-
支持多種傳送協議:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA
-
支持通過JDBC和journal提供高速的消息持久化從設計上保證了高性能的集群,客戶端-服務器,點對點
-
支持Ajax
-
支持與Axis的整合
-
可以很容易的調用內嵌JMS provider,進行測試
面試題剖析
為什么使用消息隊列
消息隊列中間件是分布式系統中重要的組件,主要解決應用耦合,異步消息,流量削鋒等問題
實現高性能,高可用,可伸縮和最終一致性架構
使用較多的消息隊列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ
解耦
看這么個場景。A 系統發送數據到 BCD 三個系統,通過接口調用發送。如果 E 系統也要這個數據呢?那如果 C 系統現在不需要了呢?A 系統負責人幾乎崩潰......

在這個場景中,A 系統跟其它各種亂七八糟的系統嚴重耦合,A 系統產生一條比較關鍵的數據,很多系統都需要 A 系統將這個數據發送過來。A 系統要時時刻刻考慮 BCDE 四個系統如果掛了該咋辦?要不要重發,要不要把消息存起來?頭發都白了啊!
如果使用 MQ,A 系統產生一條數據,發送到 MQ 里面去,哪個系統需要數據自己去 MQ 里面消費。如果新系統需要數據,直接從 MQ 里消費即可;如果某個系統不需要這條數據了,就取消對 MQ 消息的消費即可。這樣下來,A 系統壓根兒不需要去考慮要給誰發送數據,不需要維護這個代碼,也不需要考慮人家是否調用成功、失敗超時等情況。

應用解耦
第二個場景說明:用戶下單后,訂單系統需要通知庫存系統。傳統的做法是,訂單系統調用庫存系統的接口。如下圖

傳統模式的缺點:
-
假如庫存系統無法訪問,則訂單減庫存將失敗,從而導致訂單失敗
-
訂單系統與庫存系統耦合
如何解決以上問題呢?引入應用消息隊列后的方案,如下圖:

-
訂單系統:用戶下單后,訂單系統完成持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功
-
庫存系統:訂閱下單的消息,采用拉/推的方式,獲取下單信息,庫存系統根據下單信息,進行庫存操作
-
假如:在下單時庫存系統不能正常使用。也不影響正常下單,因為下單后,訂單系統寫入消息隊列就不再關心其他的后續操作了。實現訂單系統與庫存系統的應用解耦
總結:通過一個 MQ,Pub/Sub 發布訂閱消息這么一個模型,A 系統就跟其它系統徹底解耦了。
面試技巧:你需要去考慮一下你負責的系統中是否有類似的場景,就是一個系統或者一個模塊,調用了多個系統或者模塊,互相之間的調用很復雜,維護起來很麻煩。但是其實這個調用是不需要直接同步調用接口的,如果用 MQ 給它異步化解耦,也是可以的,你就需要去考慮在你的項目里,是不是可以運用這個 MQ 去進行系統的解耦。在簡歷中體現出來這塊東西,用 MQ 作解耦。
異步
再來看一個場景,A 系統接收一個請求,需要在自己本地寫庫,還需要在 BCD 三個系統寫庫,自己本地寫庫要 3ms,BCD 三個系統分別寫庫要 300ms、450ms、200ms。最終請求總延時是 3 + 300 + 450 + 200 = 953ms,接近 1s,用戶感覺搞個什么東西,慢死了慢死了。用戶通過瀏覽器發起請求,等待個 1s,這幾乎是不可接受的。

一般互聯網類的企業,對於用戶直接的操作,一般要求是每個請求都必須在 200 ms 以內完成,對用戶幾乎是無感知的。
如果使用 MQ,那么 A 系統連續發送 3 條消息到 MQ 隊列中,假如耗時 5ms,A 系統從接受一個請求到返回響應給用戶,總時長是 3 + 5 = 8ms,對於用戶而言,其實感覺上就是點個按鈕,8ms 以后就直接返回了,爽!網站做得真好,真快!

異步處理
第二個場景說明:用戶注冊后,需要發注冊郵件和注冊短信。傳統的做法有兩種 1.串行的方式;2.並行方式
(1)串行方式:將注冊信息寫入數據庫成功后,發送注冊郵件,再發送注冊短信。以上三個任務全部完成后,返回給客戶端

(2)並行方式:將注冊信息寫入數據庫成功后,發送注冊郵件的同時,發送注冊短信。以上三個任務完成后,返回給客戶端。與串行的差別是,並行的方式可以提高處理的時間

假設三個業務節點每個使用50毫秒鍾,不考慮網絡等其他開銷,則串行方式的時間是150毫秒,並行的時間可能是100毫秒。
因為CPU在單位時間內處理的請求數是一定的,假設CPU1秒內吞吐量是100次。則串行方式1秒內CPU可處理的請求量是7次(1000/150)。並行方式處理的請求量是10次(1000/100)
小結:如以上案例描述,傳統的方式系統的性能(並發量,吞吐量,響應時間)會有瓶頸。如何解決這個問題呢?
引入消息隊列,將不是必須的業務邏輯,異步處理。改造后的架構如下:

按照以上約定,用戶的響應時間相當於是注冊信息寫入數據庫的時間,也就是50毫秒。注冊郵件,發送短信寫入消息隊列后,直接返回,因此寫入消息隊列的速度很快,基本可以忽略,因此用戶的響應時間可能是50毫秒。因此架構改變后,系統的吞吐量提高到每秒20 QPS。比串行提高了3倍,比並行提高了兩倍。
削峰
每天 0:00 到 12:00,A 系統風平浪靜,每秒並發請求數量就 50 個。結果每次一到 12:00 ~ 13:00 ,每秒並發請求數量突然會暴增到 5k+ 條。但是系統是直接基於 MySQL 的,大量的請求涌入 MySQL,每秒鍾對 MySQL 執行約 5k 條 SQL。
一般的 MySQL,扛到每秒 2k 個請求就差不多了,如果每秒請求到 5k 的話,可能就直接把 MySQL 給打死了,導致系統崩潰,用戶也就沒法再使用系統了。
但是高峰期一過,到了下午的時候,就成了低峰期,可能也就 1w 的用戶同時在網站上操作,每秒中的請求數量可能也就 50 個請求,對整個系統幾乎沒有任何的壓力。
[圖片上傳失敗...(image-6444f7-1548645188187)]
如果使用 MQ,每秒 5k 個請求寫入 MQ,A 系統每秒鍾最多處理 2k 個請求,因為 MySQL 每秒鍾最多處理 2k 個。A 系統從 MQ 中慢慢拉取請求,每秒鍾就拉取 2k 個請求,不要超過自己每秒能處理的最大請求數量就 ok,這樣下來,哪怕是高峰期的時候,A 系統也絕對不會掛掉。而 MQ 每秒鍾 5k 個請求進來,就 2k 個請求出去,結果就導致在中午高峰期(1 個小時),可能有幾十萬甚至幾百萬的請求積壓在 MQ 中。

這個短暫的高峰期積壓是 ok 的,因為高峰期過了之后,每秒鍾就 50 個請求進 MQ,但是 A 系統依然會按照每秒 2k 個請求的速度在處理。所以說,只要高峰期一過,A 系統就會快速將積壓的消息給解決掉。
流量削鋒
流量削鋒也是消息隊列中的常用場景,一般在秒殺或團搶活動中使用廣泛
應用場景:秒殺活動,一般會因為流量過大,導致流量暴增,應用掛掉。為解決這個問題,一般需要在應用前端加入消息隊列。
-
可以控制活動的人數
-
可以緩解短時間內高流量壓垮應用

-
用戶的請求,服務器接收后,首先寫入消息隊列。假如消息隊列長度超過最大數量,則直接拋棄用戶請求或跳轉到錯誤頁面
-
秒殺業務根據消息隊列中的請求信息,再做后續處理
日志處理
日志處理是指將消息隊列用在日志處理中,比如Kafka的應用,解決大量日志傳輸的問題。架構簡化如下

-
日志采集客戶端,負責日志數據采集,定時寫受寫入Kafka隊列
-
Kafka消息隊列,負責日志數據的接收,存儲和轉發
-
日志處理應用:訂閱並消費kafka隊列中的日志數據
以下是新浪kafka日志處理應用案例:轉自(http://cloud.51cto.com/art/201507/484338.htm)

- Kafka:接收用戶日志的消息隊列
- Logstash:做日志解析,統一成JSON輸出給Elasticsearch
- Elasticsearch:實時日志分析服務的核心技術,一個schemaless,實時的數據存儲服務,通過index組織數據,兼具強大的搜索和統計功能
- Kibana:基於Elasticsearch的數據可視化組件,超強的數據可視化能力是眾多公司選擇ELK stack的重要原因
消息通訊
消息通訊是指,消息隊列一般都內置了高效的通信機制,因此也可以用在純的消息通訊。比如實現點對點消息隊列,或者聊天室等
點對點通訊:

客戶端A和客戶端B使用同一隊列,進行消息通訊。
聊天室通訊:

客戶端A,客戶端B,客戶端N訂閱同一主題,進行消息發布和接收。實現類似聊天室效果。
以上實際是消息隊列的兩種消息模式,點對點或發布訂閱模式。模型為示意圖,供參考。
消息隊列有什么優缺點
優點上面已經說了,就是在特殊場景下有其對應的好處,解耦、異步、削峰。
缺點有以下幾個:
-
系統可用性降低
系統引入的外部依賴越多,越容易掛掉。本來你就是 A 系統調用 BCD 三個系統的接口就好了,人 ABCD 四個系統好好的,沒啥問題,你偏加個 MQ 進來,萬一 MQ 掛了咋整,MQ 一掛,整套系統崩潰的,你不就完了?如何保證消息隊列的高可用,可以點擊這里查看。 -
系統復雜度提高
硬生生加個 MQ 進來,你怎么[保證消息沒有重復消費]?怎么[處理消息丟失的情況]?怎么保證消息傳遞的順序性?頭大頭大,問題一大堆,痛苦不已。 -
一致性問題
A 系統處理完了直接返回成功了,人都以為你這個請求就成功了;但是問題是,要是 BCD 三個系統那里,BD 兩個系統寫庫成功了,結果 C 系統寫庫失敗了,咋整?你這數據就不一致了。
所以消息隊列實際是一種非常復雜的架構,你引入它有很多好處,但是也得針對它帶來的壞處做各種額外的技術方案和架構來規避掉,做好之后,你會發現,媽呀,系統復雜度提升了一個數量級,也許是復雜了 10 倍。但是關鍵時刻,用,還是得用的。
Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么優缺點?
| 特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
|---|---|---|---|---|
| 單機吞吐量 | 萬級,比 RocketMQ、Kafka 低一個數量級 | 同 ActiveMQ | 10 萬級,支撐高吞吐 | 10 萬級,高吞吐,一般配合大數據類的系統來進行實時數據計算、日志采集等場景 |
| topic 數量對吞吐量的影響 | topic 可以達到幾百/幾千的級別,吞吐量會有較小幅度的下降,這是 RocketMQ 的一大優勢,在同等機器下,可以支撐大量的 topic | topic 從幾十到幾百個時候,吞吐量會大幅度下降,在同等機器下,Kafka 盡量保證 topic 數量不要過多,如果要支撐大規模的 topic,需要增加更多的機器資源 | ||
| 時效性 | ms 級 | 微秒級,這是 RabbitMQ 的一大特點,延遲最低 | ms 級 | 延遲在 ms 級以內 |
| 可用性 | 高,基於主從架構實現高可用 | 同 ActiveMQ | 非常高,分布式架構 | 非常高,分布式,一個數據多個副本,少數機器宕機,不會丟失數據,不會導致不可用 |
| 消息可靠性 | 有較低的概率丟失數據 | 基本不丟 | 經過參數優化配置,可以做到 0 丟失 | 同 RocketMQ |
| 功能支持 | MQ 領域的功能極其完備 | 基於 erlang 開發,並發能力很強,性能極好,延時很低 | MQ 功能較為完善,還是分布式的,擴展性好 | 功能較為簡單,主要支持簡單的 MQ 功能,在大數據領域的實時計算以及日志采集被大規模使用 |

1、Rabbitmq比kafka可靠,kafka更適合IO高吞吐的處理,比如ELK日志收集。
- 2、kafka具有高的吞吐量,內部采用消息的批量處理,zero-copy機制,數據的存儲和獲取是本地磁盤順序批量操作,具有O(1)的復雜度,消息處理的效率很高。rabbitMQ在吞吐量方面稍遜於kafka,他們的出發點不一樣,rabbitMQ支持對消息的可靠的傳遞,支持事務,不支持批量的操作;基於存儲的可靠性的要求存儲可以采用內存或者硬盤。
綜上,各種對比之后,有如下建議:
一般的業務系統要引入 MQ,最早大家都用 ActiveMQ,但是現在確實大家用的不多了,沒經過大規模吞吐量場景的驗證,社區也不是很活躍,所以大家還是算了吧,我個人不推薦用這個了;
后來大家開始用 RabbitMQ,但是確實 erlang 語言阻止了大量的 Java 工程師去深入研究和掌控它,對公司而言,幾乎處於不可控的狀態,但是確實人家是開源的,比較穩定的支持,活躍度也高;
不過現在確實越來越多的公司,會去用 RocketMQ,確實很不錯(阿里出品),但社區可能有突然黃掉的風險,對自己公司技術實力有絕對自信的,推薦用 RocketMQ,否則回去老老實實用 RabbitMQ 吧,人家有活躍的開源社區,絕對不會黃。
所以中小型公司,技術實力較為一般,技術挑戰不是特別高,用 RabbitMQ 是不錯的選擇;大型公司,基礎架構研發實力較強,用 RocketMQ 是很好的選擇。如果是大數據領域的實時計算、日志采集等場景,用 Kafka 是業內標准的,絕對沒問題,社區活躍度很高,絕對不會黃,何況幾乎是全世界這個領域的事實性規范。
https://baijiahao.baidu.com/s?id=1661973289400951766&wfr=spider&for=pc
https://blog.csdn.net/seven__________7/article/details/70225830
https://baijiahao.baidu.com/s?id=1600075220097885846&wfr=spider&for=pc
