消息隊列 ActiveMQ 、RocketMQ 、RabbitMQ 和 Kafka 如何選擇?
- 2019 年 11 月 7 日
- 筆記
消息隊列(MQ)
在百度百科中,消息隊列(MQ)是這麼解釋的:「消息隊列」是在消息的傳輸過程中保存消息的容器(可存可取)。
它是分散式系統中重要的組件,使用消息隊列主要是為了通過非同步處理提高系統性能和削峰和降低系統耦合性。
- 非同步處理:多應用對消息隊列中同一消息進行處理,應用間並發處理消息,相比較串列處理,減少處理時間;
- 應用耦合:多應用通過消息隊列對同一消息進行處理,避免調用介面失敗導致整個過程失敗;
- 限流消峰:廣泛應用於秒查或搶購活動中,避免某一刻流量過導致應用系統掛掉的情況;
目前使用較多的消息隊列有 ActiveMQ 、RocketMQ 、RabbitMQ 和 Kafka 等。
消息隊列的兩種模式
消息隊列包括兩種模式:點對點模式 和 發布/訂閱模式。
1)點對點模式
點對點模式下包括三個角色:
- 消息隊列
- 發送者 (生產者)
- 接收者(消費者)

消息發送者生產消息發送到queue中,然後消息接收者從queue中取出並且消費消息。消息被消費以後,queue中不再有存儲,所以消息接收者不可能消費到已經被消費的消息。
點對點模式特點:
- 每個消息只有一個接收者(Consumer)(即一旦被消費,消息就不再在消息隊列中);
- 發送者和接收者間沒有依賴性,發送者發送消息之後,不管有沒有接收者在運行,都不會影響到發送者下次發送消息;
- 接收者在成功接收消息之後需向隊列應答成功,以便消息隊列刪除當前接收的消息;
2)發布/訂閱模式
發布/訂閱模式下包括三個角色:
- 角色主題(Topic)
- 發布者(Publisher)
- 訂閱者(Subscriber)

發布者將消息發送到Topic,系統將這些消息傳遞給多個訂閱者。
發布/訂閱模式特點:
- 每個消息可以有多個訂閱者;
- 發布者和訂閱者之間有時間上的依賴性。針對某個主題(Topic)的訂閱者,它必須創建一個訂閱者之後,才能消費發布者的消息。
- 為了消費消息,訂閱者需要提前訂閱該角色主題,並保持在線運行;
非同步處理
具體場景:用戶為了使用某個應用,進行註冊,系統需要發送註冊郵件和註冊簡訊。
對於該流程有兩種處理方式:並行和串列。
1)串列處理:寫入註冊資訊後,先發送註冊郵件,再發送註冊簡訊。

這種方式下,需要等發送簡訊處理完成後才完成註冊。
2)並行處理:寫入註冊資訊後,同時處理髮郵件和發簡訊。

這種方式下,需要等發送簡訊和發送郵件處理完成後才完成註冊。
假設上面三個子系統處理耗時均為:50ms,且不考慮網路延遲,系統卡頓等因素,則總的處理時間為:
串列:50ms + 50ms + 50 ms = 150ms
並行:50ms + 50ms = 100ms
使用消息隊列結果將如何呢?

若使用消息隊列,寫入完註冊資訊後,再將資訊寫入消息隊列就能直接返回成功給客戶端了,然後註冊完成。
現在總的響應時間依賴於寫入消息隊列的時間,而寫入消息隊列的時間是很快的,基本可以忽略不計。因此總的處理時間相比串列提高了 2 倍,相比並行提高了 1 倍。
應用解耦
具體場景:A 系統每次產生數據時,都要將數據發給 BC 兩個系統(通過介面調用)。這個時候新增了一個 D 系統,如下圖:

在這個場景中,A 系統和其他幾個系統亂七八糟的耦合在一起,當 A 系統中產生一條數據時,需要將數據發給各個系統。
每次項目中新增了系統,A 系統都需要修改程式碼,還要時刻擔心那個系統掛掉了,資訊沒發送過去要不要重發,那個系統又不要該數據了,這時求 A 系統負責人的心理陰影…
如果使用 MQ,A 系統產生一條數據後,只需要插入到 MQ 裡面去,那個系統需要就去 MQ 里消費。如果新增了一個系統,那麼訂閱 MQ 的消息即可;同理那個系統不再需要該數據,那麼只要取消訂閱就行了。

通過一個 MQ, Pub/Sub 發布訂閱消息模型,A 系統就跟其他系統實現解耦了。
限流削峰
具體場景:某電商網站開展秒殺活動,一般由於瞬間訪問暴增,伺服器收到請求過大,可能出現無法處理請求或崩潰的情況。

加入消息隊列後,系統就可以從消息隊列中讀取數據,相當於做了一次緩衝,超出系統處理之外的請求會積壓在消息隊列中,等高峰期已過,就會快速將積壓在隊列中的數據處理完。
消息隊列有什麼優缺點
優點上面已經說了,就是在特殊場景下有其對應的好處,解耦、非同步、削峰。
缺點有以下幾個:
- 系統可用性降低 系統引入的外部依賴越多,越容易掛掉。本來你就是 A 系統調用 BCD 三個系統的介面就好了,人 ABCD 四個系統好好的,沒啥問題,你偏加個 MQ 進來,萬一 MQ 掛了咋整,MQ 一掛,整套系統崩潰的,你不就完了?如何保證消息隊列的高可用,可以點擊這裡查看。
- 系統複雜度提高 硬生生加個 MQ 進來,你怎麼保證消息沒有重複消費?怎麼處理消息丟失的情況?怎麼保證消息傳遞的順序性?頭大頭大,問題一大堆,痛苦不已。
- 一致性問題 A 系統處理完了直接返回成功了,人都以為你這個請求就成功了;但是問題是,要是 BCD 三個系統那裡,BD 兩個系統寫庫成功了,結果 C 系統寫庫失敗了,咋整?你這數據就不一致了。
所以消息隊列實際是一種非常複雜的架構,你引入它有很多好處,但是也得針對它帶來的壞處做各種額外的技術方案和架構來規避掉,做好之後,系統複雜度提升了一個數量級,也許是複雜了 10 倍。但是關鍵時刻,用,還是得用的。
RabbitMQ/ActiveMQ/RocketMQ/Kafka對比
這裡列舉了上述四種消息隊列的差異對比(圖片來源:https://www.cnblogs.com/javalyy/p/8856731.html):

圖片來源(https://doocs.gitee.io/advanced-java/#/docs/high-concurrency/why-mq)

總結
一般業務系統要引入 MQ,最早大家都是用 ActiveMQ,但是現在大家用的不多了,沒經過大規模吞吐量場景的驗證,社區也不是很活躍,不推薦使用。
中小型公司,技術實力較為一般,技術挑戰不是特別高,用 RabbitMQ 是不錯的選擇,大型公司,基礎架構研發實力較強,用 RocketMQ 是很好的選擇。
如果是高性能分散式、大數據領域的實時計算、日誌採集等場景,用 Kafka 是業內標準。
廣泛來說,電商、金融等對事務性要求很高的,可以考慮RabbitMQ和RocketMQ,對性能要求高的可考慮Kafka。
如果你覺得文章不錯,歡迎在看、轉發、讚賞一條龍,你的支援就是我最大的動力。