中華石杉-- --訊息佇列的筆記

2020-08-12 16:50:16

訊息佇列

爲什麼使用訊息佇列

面試官問你這個問題,期望的一個回答是說,你們公司有個什麼業務場景,這個業務場景有個什麼技術挑戰,如果不用 MQ 可能會很麻煩,但是你現在用了 MQ 之後帶給了你很多的好處。

先說一下訊息佇列常見的使用場景吧,其實場景有很多,但是比較核心的有 3 個:解耦、非同步、削峯。
解耦:
看這麼個場景。A 系統發送數據到 BCD 三個系統,通過介面呼叫發送。如果 E 系統也要這個數據呢?那如果 C 系統現在不需要了呢?A 系統負責人幾乎崩潰…

在這個場景中,A 系統跟其它各種亂七八糟的系統嚴重耦合,A 系統產生一條比較關鍵的數據,很多系統都需要 A 系統將這個數據發送過來。A 系統要時時刻刻考慮 BCDE 四個系統如果掛了該咋辦?要不要重發,要不要把訊息存起來?頭髮都白了啊!

如果使用 MQ,A 系統產生一條數據,發送到 MQ 裏面去,哪個系統需要數據自己去 MQ 裏面消費。如果新系統需要數據,直接從 MQ 裡消費即可;如果某個系統不需要這條數據了,就取消對 MQ 訊息的消費即可。這樣下來,A 系統壓根兒不需要去考慮要給誰發送數據,不需要維護這個程式碼,也不需要考慮人家是否呼叫成功、失敗超時等情況。
非同步:
再來看一個場景,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 以後就直接返回了,爽!網站做得真好,真快!
削峯
每天 0:00 到 12:00,A 系統風平浪靜,每秒併發請求數量就 50 個。結果每次一到 12:00 ~ 13:00 ,每秒併發請求數量突然會暴增到 5k+ 條。但是系統是直接基於 MySQL 的,大量的請求涌入 MySQL,每秒鐘對 MySQL 執行約 5k 條 SQL。

一般的 MySQL,扛到每秒 2k 個請求就差不多了,如果每秒請求到 5k 的話,可能就直接把 MySQL 給打死了,導致系統崩潰,使用者也就沒法再使用系統了。

但是高峯期一過,到了下午的時候,就成了低峯期,可能也就 1w 的使用者同時在網站上操作,每秒中的請求數量可能也就 50 個請求,對整個系統幾乎沒有任何的壓力。

如果使用 MQ,每秒 5k 個請求寫入 MQ,A 系統每秒鐘最多處理 2k 個請求,因爲 MySQL 每秒鐘最多處理 2k 個。A 系統從 MQ 中慢慢拉取請求,每秒鐘就拉取 2k 個請求,不要超過自己每秒能處理的最大請求數量就 ok,這樣下來,哪怕是高峯期的時候,A 系統也絕對不會掛掉。而 MQ 每秒鐘 5k 個請求進來,就 2k 個請求出去,結果就導致在中午高峯期(1 個小時),可能有幾十萬甚至幾百萬的請求積壓在 MQ 中。

這個短暫的高峯期積壓是 ok 的,因爲高峯期過了之後,每秒鐘就 50 個請求進 MQ,但是 A 系統依然會按照每秒 2k 個請求的速度在處理。所以說,只要高峯期一過,A 系統就會快速將積壓的訊息給解決掉。

訊息佇列有什麼優缺點

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

  • 系統可用性降低
    系統引入的外部依賴越多,越容易掛掉。本來你就是 A 系統呼叫 BCD 三個系統的介面就好了,人 ABCD 四個系統好好的,沒啥問題,你偏加個 MQ 進來,萬一 MQ 掛了咋整,MQ 一掛,整套系統崩潰的,你不就完了?如何保證訊息佇列的高可用,可以點選這裏檢視。
  • 系統複雜度提高
    硬生生加個 MQ 進來,你怎麼保證訊息沒有重複消費?怎麼處理訊息丟失的情況?怎麼保證訊息傳遞的順序性?頭大頭大,問題一大堆,痛苦不已。
  • 一致性問題
    A 系統處理完了直接返回成功了,人都以爲你這個請求就成功了;但是問題是,要是 BCD 三個系統那裏,BD 兩個系統寫庫成功了,**結果 C 系統寫庫失敗了,**咋整?你這數據就不一致了。

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

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

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

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

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

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

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

如何保證訊息佇列的高可用?

這個問題這麼問是很好的,因爲不能問你 Kafka 的高可用性怎麼保證?ActiveMQ 的高可用性怎麼保證?一個面試官要是這麼問就顯得很沒水平,人家可能用的就是 RabbitMQ,沒用過 Kafka,你上來問人家 Kafka 幹什麼?這不是擺明了刁難人麼。

所以有水平的面試官,問的是 MQ 的高可用性怎麼保證?這樣就是你用過哪個 MQ,你就說說你對那個 MQ 的高可用性的理解。

RabbitMQ 的高可用性

RabbitMQ 是比較有代表性的,因爲是基於主從(非分佈式)做高可用性的,我們就以 RabbitMQ 爲例子講解第一種 MQ 的高可用性怎麼實現。

RabbitMQ 有三種模式:單機模式、普通叢集模式、映象叢集模式。

  • 單機模式
    單機模式,就是 Demo 級別的,一般就是你本地啓動了玩玩兒的