1. 什么是MQ
MQ 就是消息隊列。是軟件和軟件進行通信的中間件產品
2. MQ的優點
異步處理 - 相比于傳統的串行、并行方式,提高了系統吞吐量。
應用解耦 - 系統間通過消息通信,不用關心其他系統的處理。
流量削鋒 - 可以通過消息隊列長度控制請求量;可以緩解短時間內的高并發請求。
日志處理 - 解決大量日志傳輸。
消息通訊 - 消息隊列一般都內置了高效的通信機制,因此也可以用在純的消息通訊。比如實
現點對點消息隊列,或者聊天室等。
3. 解耦、異步、削峰是什么?。
解耦 : 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 。
削峰 :減少高峰時期對服務器壓力。
4. 消息隊列有什么缺點
缺點有以下幾個:
1. 系統可用性降低
本來系統運行好好的,現在你非要加入個消息隊列進去,那消息隊列掛了,你的系統不是呵
呵了。因此,系統可用性會降低;
2. 系統復雜度提高
加入了消息隊列,要多考慮很多方面的問題,比如:一致性問題、如何保證消息不被重復消
費、如何保證消息可靠性傳輸等。因此,需要考慮的東西更多,復雜性增大。
3. 一致性問題
A 系統處理完了直接返回成功了,人都以為你這個請求就成功了;但是問題是,要是 BCD 三
個系統那里, BD 兩個系統寫庫成功了,結果 C 系統寫庫失敗了,咋整?你這數據就不一致
了。
所以消息隊列實際是一種非常復雜的架構,你引入它有很多好處,但是也得針對它帶來的壞處做各種額外的技術方案和架構來規避掉,做好之后,你會發現,媽呀,系統復雜度提升了一個數量級,也許是復雜了 10 倍。但是關 鍵時刻,用,還是得用的。
5. 你們公司生產環境用的是什么消息中間件?
這個首先你可以說下你們公司選用的是什么消息中間件,比如用的是 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 中間件使用場景中較少采用。
6. Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么優缺點?
綜上,各種對比之后,有如下建議:
一般的業務系統要引入 MQ ,最早大家都用 ActiveMQ ,但是現在確實大家用的不多了,沒經過大
規模吞吐量場景的驗證,社區也不是很活躍,所以大家還是算了吧,我個人不推薦用這個了;
后來大家開始用 RabbitMQ ,但是確實 erlang 語言阻止了大量的 Java 工程師去深入研究和掌控
它,對公司而言,幾乎處于不可控的狀態,但是確實人家是開源的,比較穩定的支持,活躍度也
高;
不過現在確實越來越多的公司會去用 RocketMQ ,確實很不錯,畢竟是阿里出品,但社區可能有
突然黃掉的風險(目前 RocketMQ 已捐給 Apache ,但 GitHub 上的活躍度其實不算高)對自己公
司技術實力有絕對自信的,推薦用 RocketMQ ,否則回去老老實實用 RabbitMQ 吧,人家有活躍
的開源社區,絕對不會黃。
所以 中小型公司 ,技術實力較為一般,技術挑戰不是特別高,用 RabbitMQ 是不錯的選擇; 大型
公司 ,基礎架構研發實力較強,用 RocketMQ 是很好的選擇。
如果是 大數據領域 的實時計算、日志采集等場景,用 Kafka 是業內標準的,絕對沒問題,社區活
躍度很高,絕對不會黃,何況幾乎是全世界這個領域的事實性規范。
7. MQ 有哪些常見問題?如何解決這些問題?
MQ 的常見問題有:
消息的順序問題
消息的重復問題
消息的順序問題
消息有序指的是可以按照消息的發送順序來消費。
假如生產者產生了 2 條消息: M1 、 M2 ,假定 M1 發送到 S1 , M2 發送到 S2 ,如果要保證 M1 先
于 M2 被消費,怎么做?
解決方案:
1. 保證生產者 - MQServer - 消費者是一對一對一的關系
缺陷:
并行度就會成為消息系統的瓶頸(吞吐量不夠)
更多的異常處理,比如:只要消費端出現問題,就會導致整個處理流程阻塞,我們不得不花
費更多的精力來解決阻塞的問題。 (
2 )通過合理的設計或者將問題分解來規避。
不關注亂序的應用實際大量存在
隊列無序并不意味著消息無序 所以從業務層面來保證消息的順序而不僅僅是依賴于消息系
統,是一種更合理的方式。
消息的重復問題
造成消息重復的根本原因是:網絡不可達。
所以解決這個問題的辦法就是繞過這個問題。那么問題就變成了:如果消費端收到兩條一樣的消
息,應該怎樣處理?
消費端處理消息的業務邏輯保持冪等性。只要保持冪等性,不管來多少條重復消息,最后處理的結
果都一樣。保證每條消息都有唯一編號且保證消息處理成功與去重表的日志同時出現。利用一張日
志表來記錄已經處理成功的消息的 ID ,如果新到的消息 ID 已經在日志表中,那么就不再處理這條
消息。
8. 什么是RabbitMQ?
RabbitMQ 是一款開源的, Erlang 編寫的,消息中間件; 最大的特點就是消費并不需要確保提供方
存在 , 實現了服務之間的高度解耦 可以用它來:解耦、異步、削峰。
9. rabbitmq 的使用場景
服務間異步通信
順序消費
定時任務
請求削峰
10. 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 的唯一的線路。
11. RabbitMQ的工作模式
一.simple模式(即最簡單的收發模式)
1. 消息產生消息,將消息放入隊列
2. 消息的消費者 (consumer) 監聽 消息隊列 , 如果隊列中有消息 , 就消費掉 , 消息被拿走后 , 自動從隊列中
刪除 ( 隱患 消息可能沒有被消費者正確處理 , 已經從隊列中消失了 , 造成消息的丟失,這里可以設置
成手動的 ack, 但如果設置成手動 ack ,處理完后要及時發送 ack 消息給隊列,否則會造成內存溢
出 ) 。
二.work工作模式(資源的競爭)
1. 消息產生者將消息放入隊列消費者可以有多個 , 消費者 1, 消費者 2 同時監聽同一個隊列 , 消息被消費。
C1 C2 共同爭搶當前的消息隊列內容 , 誰先拿到誰負責消費消息 ( 隱患:高并發情況下 , 默認會產生某
一個消息被多個消費者共同使用 , 可以設置一個開關 (syncronize) 保證一條消息只能被一個消費者
使用 ) 。
三.publish/subscribe發布訂閱(共享資源)
1. 每個消費者監聽自己的隊列;
2. 生產者將消息發給 broker ,由交換機將消息轉發到綁定此交換機的每個隊列,每個綁定交換機的
隊列都將接收到消息。
四.routing路由模式
1. 消息生產者將消息發送給交換機按照路由判斷 , 路由是字符串 (info) 當前產生的消息攜帶路由字符
( 對象的方法 ), 交換機根據路由的 key, 只能匹配上路由 key 對應的消息隊列 , 對應的消費者才能消費消
息 ;
2. 根據業務功能定義路由字符串
3. 從系統的代碼邏輯中獲取對應的功能字符串 , 將消息任務扔到對應的隊列中。
4. 業務場景 :error 通知 ;EXCEPTION; 錯誤通知的功能 ; 傳統意義的錯誤通知 ; 客戶通知 ; 利用 key 路由 , 可
以將程序中的錯誤封裝成消息傳入到消息隊列中 , 開發者可以自定義消費者 , 實時接收錯誤 ;
五.topic 主題模式(路由模式的一種)
1. 星號井號代表通配符
2. 星號代表多個單詞 , 井號代表一個單詞
3. 路由功能添加模糊匹配
4. 消息產生者產生消息 , 把消息交給交換機
5. 交換機根據 key 的規則模糊匹配到對應的隊列 , 由隊列的監聽消費者接收消息消費
(在我的理解看來就是 routing 查詢的一種模糊匹配,就類似 sql 的模糊查詢方式)
12. 如何保證RabbitMQ消息的順序性?
拆分多個 queue( 消息隊列 ) ,每個 queue( 消息隊列 ) 一個 consumer( 消費者 ) ,就是多一些 queue
( 消息隊列 ) 而已,確實是麻煩點; 或者就一個 queue ( 消息隊列 ) 但是對應一個 consumer( 消費者 ) ,然后這個 consumer( 消費者 ) 內
部用內存隊列做排隊,然后分發給底層不同的 worker 來處理。
13. 消息如何分發?
若該隊列至少有一個消費者訂閱,消息將以循環(
round-robin )的方式發送給消費者。每條消息
只會分發給一個訂閱的消費者(前提是消費者能夠正常處理消息并進行確認)。通過路由可實現多
消費的功能
14. 消息怎么路由?
消息提供方 -> 路由 -> 一至多個隊列消息發布到交換器時,消息將擁有一個路由鍵(routing key),
在消息創建時設定。通過隊列路由鍵,可以把隊列綁定到交換器上。消息到達交換器后,
RabbitMQ 會將消息的路由鍵與隊列的路由鍵進行匹配(針對不同的交換器有不同的路由規則);
常用的交換器主要分為一下三種:
1. fanout :如果交換器收到消息,將會廣播到所有綁定的隊列上
2. direct :如果路由鍵完全匹配,消息就被投遞到相應的隊列
3. topic :可以使來自不同源頭的消息能夠到達同一個隊列。 使用 topic 交換器時,可以使用通
配符
15. 消息基于什么傳輸?
由于 TCP 連接的創建和銷毀開銷較大,且并發數受系統資源限制,會造成性能瓶頸。 RabbitMQ
使用信道的方式來傳輸數據。信道是建立在真實的 TCP 連接內的虛擬連接,且每條 TCP 連接上的
信道數量沒有限制。
16. 如何保證消息不被重復消費?或者說,如何保證消息消費時的冪等性?
先說為什么會重復消費:正常情況下,消費者在消費消息的時候,消費完畢后,會發送一個確認消
息給消息隊列,消息隊列就知道該消息被消費了,就會將該消息從消息隊列中刪除;
但是因為網絡傳輸等等故障,確認信息沒有傳送到消息隊列,導致消息隊列不知道自己已經消費過
該消息了,再次將消息分發給其他的消費者。
針對以上問題,一個解決思路是:保證消息的唯一性,就算是多次傳輸,不要讓消息的多次消費帶
來影響;保證消息等冪性;
比如:在寫入消息隊列的數據做唯一標示,消費消息時,根據唯一標識判斷是否消費過;
假設你有個系統,消費一條消息就往數據庫里插入一條數據,要是你一個消息重復兩次,你
不就插入了兩條,這數據不就錯了?但是你要是消費到第二次的時候,自己判斷一下是否已
經消費過了,若是就直接扔了,這樣不就保留了一條數據,從而保證了數據的正確性。
17. 如何確保消息正確地發送至 RabbitMQ? 如何確保消息接收方消費了消息?
發送方確認模式
將信道設置成 confifirm 模式(發送方確認模式),則所有在信道上發布的消息都會被指派一個唯
一的 ID 。
一旦消息被投遞到目的隊列后,或者消息被寫入磁盤后(可持久化的消息),信道會發送一個確認
給生產者(包含消息唯一 ID )。
如果 RabbitMQ 發生內部錯誤從而導致消息丟失,會發送一條 nack ( notacknowledged ,未確
認)消息。
發送方確認模式是異步的,生產者應用程序在等待確認的同時,可以繼續發送消息。當確認消息到
達生產者應用程序,生產者應用程序的回調方法就會被觸發來處理確認消息。
接收方確認機制
消費者接收每一條消息后都必須進行確認(消息接收和消息確認是兩個不同操作)。只有消費者確
認了消息, RabbitMQ 才能安全地把消息從隊列中刪除。
這里并沒有用到超時機制, RabbitMQ 僅通過 Consumer 的連接中斷來確認是否需要重新發送消
息。也就是說,只要連接不中斷, RabbitMQ 給了 Consumer 足夠長的時間來處理消息。保證數
據的最終一致性;
下面羅列幾種特殊情況
如果消費者接收到消息,在確認之前斷開了連接或取消訂閱, RabbitMQ 會認為消息沒有被分發,
然后重新分發給下一個訂閱的消費者。(可能存在消息重復消費的隱患,需要去重)
如果消費者接收到消息卻沒有確認消息,連接也未斷開,則 RabbitMQ 認為該消費者繁忙,將不
會給該消費者分發更多的消息。
18. 如何保證RabbitMQ消息的可靠傳輸?
消息不可靠的情況可能是消息丟失,劫持等原因;
丟失又分為:生產者丟失消息、消息列表丟失消息、消費者丟失消息;
1. 生產者丟失消息 :從生產者弄丟數據這個角度來看, RabbitMQ 提供 transaction 和 confifirm 模式來
確保生產者不丟消息;
transaction 機制就是說:發送消息前,開啟事務( channel.txSelect() ) , 然后發送消息,如果發送
過程中出現什么異常,事務就會回滾( channel.txRollback() ) , 如果發送成功則提交事務
( channel.txCommit() )。然而,這種方式有個缺點:吞吐量下降;
confifirm 模式用的居多:一旦 channel 進入 confifirm 模式,所有在該信道上發布的消息都將會被指派
一個唯一的 ID (從 1 開始),一旦消息被投遞到所有匹配的隊列之后;
rabbitMQ 就會發送一個 ACK 給生產者(包含消息的唯一 ID ),這就使得生產者知道消息已經正確
到達目的隊列了;
如果 rabbitMQ 沒能處理該消息,則會發送一個 Nack 消息給你,你可以進行重試操作。
2. 消息隊列丟數據 :消息持久化。
處理消息隊列丟數據的情況,一般是開啟持久化磁盤的配置。
這個持久化配置可以和 confifirm 機制配合使用,你可以在消息持久化磁盤后,再給生產者發送一個
Ack 信號。
這樣,如果消息持久化磁盤之前, rabbitMQ 陣亡了,那么生產者收不到 Ack 信號,生產者會自動
重發。
那么如何持久化呢?
這里順便說一下吧,其實也很容易,就下面兩步
1. 將 queue 的持久化標識 durable 設置為 true, 則代表是一個持久的隊列
2. 發送消息的時候將 deliveryMode=2
這樣設置以后,即使 rabbitMQ 掛了,重啟后也能恢復數據
3. 消費者丟失消息 :消費者丟數據一般是因為采用了自動確認消息模式,改為手動確認消息即可!
消費者在收到消息之后,處理消息之前,會自動回復 RabbitMQ 已收到消息;
如果這時處理消息失敗,就會丟失該消息;
解決方案:處理消息成功后,手動回復確認消息。
19. 為什么不應該對所有的 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 )。另
外一種處理原則是:僅對關鍵消息作持久化處理(根據業務重要程度),且應該保證關鍵消息的量
不會導致性能瓶頸。
20. 如何保證高可用的?RabbitMQ 的集群
RabbitMQ 是比較有代表性的,因為是基于主從(非分布式)做高可用性的,我們就以 RabbitMQ
為例子講解第一種 MQ 的高可用性怎么實現。 RabbitMQ 有三種模式:單機模式、普通集群模
式、鏡像集群模式。
1. 單機模式 ,就是 Demo 級別的,一般就是你本地啟動了玩玩兒的 ? ,沒人生產用單機模式
2. 普通集群模式 :
意思就是在多臺機器上啟動多個 RabbitMQ 實例,每個機器啟動一個。
你創建的 queue ,只會放在一個 RabbitMQ 實例上,但是每個實例都同步 queue 的元數據
(元數據可以認為是 queue 的一些配置信息,通過元數據,可以找到 queue 所在實例)。
你消費的時候,實際上如果連接到了另外一個實例,那么那個實例會從 queue 所在實例上拉
取數據過來。這方案主要是提高吞吐量的,就是說讓集群中多個節點來服務某個 queue 的讀
寫操作。
3. 鏡像集群模式 :
這種模式,才是所謂的 RabbitMQ 的高可用模式。跟普通集群模式不一樣的是,在鏡像集群
模式下,你創建的 queue ,無論元數據還是 queue 里的消息都會存在于多個實例上,就是
說,每個 RabbitMQ 節點都有這個 queue 的一個完整鏡像,包含 queue 的全部數據的意
思。然后每次你寫消息到 queue 的時候,都會自動把消息同步到多個實例的 queue 上。
RabbitMQ 有很好的管理控制臺,就是在后臺新增一個策略,這個策略是鏡像集群模式的策
略,指定的時候是可以要求數據同步到所有節點的,也可以要求同步到指定數量的節點,再
次創建 queue 的時候,應用這個策略,就會自動將數據同步到其他的節點上去了。
這樣的好處在于,你任何一個機器宕機了,沒事兒,其它機器(節點)還包含了這個 queue
的完整數據,別的 consumer 都可以到其它節點上去消費數據。壞處在于,第一,這個性能
開銷也太大了吧,消息需要同步到所有機器上,導致網絡帶寬壓力和消耗很重! RabbitMQ
一個 queue 的數據都是放在一個節點里的,鏡像集群下,也是每個節點都放這個 queue 的
完整數據。
21. 如何解決消息隊列的延時以及過期失效問題?消息隊列滿了以后該怎么處理?有幾百萬消息持續積壓幾小時,怎么辦?
消息積壓處理辦法:臨時緊急擴容:
先修復 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 都快寫滿
了,咋辦?這個還有別的辦法嗎?沒有,誰讓你第一個方案執行的太慢了,你臨時寫程序,接入數
據來消費,消費一個丟棄一個,都不要了,快速消費掉所有的消息。然后走第二個方案,到了晚上
再補數據吧。
22. 設計MQ思路
比如說這個消息隊列系統,我們從以下幾個角度來考慮一下:
首先這個 mq 得支持可伸縮性吧,就是需要的時候快速擴容,就可以增加吞吐量和容量,那怎么
搞?設計個分布式的系統唄,參照一下 kafka 的設計理念, broker -> topic -> partition ,每個
partition 放一個機器,就存一部分數據。如果現在資源不夠了,簡單啊,給 topic 增加
partition ,然后做數據遷移,增加機器,不就可以存放更多數據,提供更高的吞吐量了?
其次你得考慮一下這個 mq 的數據要不要落地磁盤吧?那肯定要了,落磁盤才能保證別進程掛了
數據就丟了。那落磁盤的時候怎么落啊?順序寫,這樣就沒有磁盤隨機讀寫的尋址開銷,磁盤順序
讀寫的性能是很高的,這就是 kafka 的思路。
其次你考慮一下你的 mq 的可用性啊?這個事兒,具體參考之前可用性那個環節講解的 kafka 的
高可用保障機制。多副本 -> leader & follower -> broker 掛了重新選舉 leader 即可對外服務。
能不能支持數據 0 丟失啊?可以呀,有點復雜的。