消息隊列

什麼是 MQ?

Message Queue(MQ),消息隊列中間件。不少人都說:MQ 經過將消息的發送和接收分離來實現應用程序的異步和解偶,這個給人的直覺是——MQ 是異步的,用來解耦的,可是這個只是 MQ 的效果而不是目的。MQ 真正的目的是爲了通信,屏蔽底層複雜的通信協議,定義了一套應用層的、更加簡單的通信協議。一個分佈式系統中兩個模塊之間通信要麼是 HTTP,要麼是本身開發的 TCP,可是這兩種協議其實都是原始的協議。HTTP 協議很難實現兩端通信——模塊 A 能夠調用 B,B 也能夠主動調用 A,若是要作到這個兩端都要背上 WebServer,並且還不支持長鏈接(HTTP 2.0 的庫根本找不到)。TCP 就更加原始了,粘包、心跳、私有的協議,想想頭皮就發麻。MQ 所要作的就是在這些協議之上構建一個簡單的「協議」——生產者/消費者模型。MQ 帶給個人「協議」不是具體的通信協議,而是更高層次通信模型。它定義了兩個對象——發送數據的叫生產者;接收數據的叫消費者, 提供一個 SDK 讓咱們能夠定義本身的生產者和消費者實現消息通信而無視底層通信協議數據庫

消息隊列的流派

  • 有 Broker 的MQ緩存

    生產者/消費者模式必定伴隨着者觀察者模式。安全

    這個流派一般有一臺服務器做爲 Broker,全部的消息都經過它中轉。生產者把消息發送給它就結束本身的任務了,Broker 則把消息主動推送給消費者(或者消費者主動輪詢)服務器

    • 重 Topic網絡

      kafka、JMS(ActiveMQ)就屬於這個流派,生產者會發送 key 和數據到 Broker,由 Broker 比較 key 以後決定給哪一個消費者。這種模式是咱們最多見的模式,是咱們對 MQ 最多的印象。在這種模式下一個 topic 每每是一個比較大的概念,甚至一個系統中就可能只有一個 topic,topic 某種意義上就是 queue,生產者發送 key 至關於說:「hi,把數據放到 key 的隊列中」

      kafka 會丟數據。架構

    雖然架構同樣可是 kafka 的性能要比 jms 的性能不知道高到多少倍,因此基本這種類型的 MQ 只有 kafka 一種備選方案。若是你須要一條暴力的數據流(在意性能而非靈活性)那麼 kafka 是最好的選擇負載均衡

  • 輕 Topic異步

    這種的表明是 RabbitMQ(或者說是 AMQP)。生產者發送 key 和數據,消費者定義訂閱的隊列,Broker 收到數據以後會經過必定的邏輯計算出 key 對應的隊列,而後把數據交給隊列分佈式

AMQP 中有四種 exchange性能

  • Direct exchange:key 就等於 queue
  • Fanout exchange:無視 key,給全部的 queue 都來一份
  • Topic exchange:key 能夠用「寬字符」模糊匹配 queue
  • Headers exchange:無視 key,經過查看消息的頭部元數據來決定發給那個 queue(AMQP 頭部元數據很是豐富並且能夠自定義)

  • 無 Broker 的 MQ

    無 Broker 的 MQ 的表明是 ZeroMQ。該做者很是睿智,他很是敏銳的意識到——MQ 是更高級的 Socket,它是解決通信問題的。因此 ZeroMQ 被設計成了一個「庫」而不是一箇中間件,這種實現也能夠達到——沒有 Broker 的目的

    節點之間通信的消息都是發送到彼此的隊列中,每一個節點都既是生產者又是消費者。ZeroMQ 作的事情就是封裝出一套相似於 Socket 的 API 能夠完成發送數據,讀取數據

    ZeroMQ 其實就是一個跨語言的、重量級的 Actor 模型郵箱庫。你能夠把本身的程序想象成一個 Actor,ZeroMQ 就是提供郵箱功能的庫;ZeroMQ 能夠實現同一臺機器的 RPC 通信也能夠實現不一樣機器的 TCP、UDP 通信,若是你須要一個強大的、靈活、野蠻的通信能力,別猶豫 ZeroMQ

爲何要用 MQ?

  • 異步處理
    好比用戶註冊後須要發送短信和郵件,新手會怎麼寫那?新手會把全部業務寫成串行化,註冊成功後發郵件,發短信。缺點是會把接口的時間拖的很慢。

    如何解決和優化那?
    發送郵件和短信徹底能夠異步處理,註冊成功,發個消息消息中間件後直接返回,讓中間件去異步的發郵件和短信。
  • 應用解藕
    好比在分佈式系統中,咱們有訂單服務,庫存服務等等。咱們訂單服務下完單後經過 rpc 調用庫存系統。假如你的庫存系統掛了,你的訂單是否是就失敗了?
    解決辦法仍是加一個消息中間件。使用發佈訂閱模式。
    系統的耦合性越高,容錯就越低。好比訂單系統要去調用支付系統,庫存系統和物流系統,若是任何一個系統高異常都會影響用戶體驗。
    加入 MQ 後,好比物流系統出現異常,能夠將物流消息暫時存在 MQ 中,不影響用戶正常下單,等物流系統恢復後繼續執行(分佈式事務的概念)。

  • 流量削峯
    應用系統若是遇到系統請求流量的瞬間猛漲,有可能會將系統壓垮,有了消息隊列能夠將大量請求緩存起來,分散到很長一段時間處理,這樣能夠大大提升系統的穩定性和用戶體驗。
    若是業務系統正常時段的 QPS 是 1000,流量最高峯是 10000,請求超過某個閥值後對流量進行削峯處理。

  • MQ 的優勢和缺點
    優勢:解藕,削峯,數據分發。

缺點:

  1. 系統可用性下降
    系統引入的外部依賴越多,系統穩定性越差。一旦 MQ 宕機,就會對業務形成影響。
  2. 系統複雜度提升
  3. 一致性問題
    A 系統處理完業務,經過 MQ 給 B,C,D 三個系統發消息數據,若是 B,C 處理成功,D 系統處理失敗。如何保證消息數據處理的一致性?

Push 和 Pull 模型

  • Push 模型,即當 Producer 發出的消息到達後,服務端立刻將這條消息投遞給 Consumer。
  • Pull 模型,指的是服務端接收到這條消息後什麼也不作,只是等着 Consumer 主動到本身這裏來讀,即 Consumer 這裏有一個 「拉取」 的動做。

RocketMQ 的角色介紹

  • Producer:消息發送者。
  • Consumer:消息接收者。
  • Broker:暫存和傳輸消息,像郵局。
  • NameServer:管理 Broker,命名服務。
  • Topic:區分消息的種類,一個發送者能夠發送消息給一個或者多個多個 Topic,一個消息的接受者能夠訂閱一個或者多個 Topic 消息。
  • Message Queue:至關因而 Topic 的分區,用於並行發送和接收消息。

持久化

  1. 消息生成者發送消息
  2. MQ 收到消息,將消息進行持久化,在存儲中新增一條記錄
  3. 返回 Ack 給生產者
  4. MQ push 消息給對應的消費者,而後等待消費者返回 Ack
  5. 若是消息消費者在指定時間內返回 Ack,那麼 MQ 會認爲消息消費成功,在存儲中刪除消息,即執行第 6 步。若是 MQ 在指定時間內沒有收到 Ack,則認爲消息消費失敗,會嘗試從新 push 消息,重複執行 4,5,6 步驟。
  6. MQ 刪除消息

存儲介質

  • 關係型數據庫(ActiveMQ,數據庫的 IO 讀寫性能每每會出現瓶頸)。
  • 文件系統(RocketMQ/Kafka/RabbitMQ 均採用的是消息刷盤至所部屬虛擬機/物理機的文件系統來作初始化,刷盤能夠採用同步/異步兩種模式)。
  • RocketMQ 寫文件時採用順序寫,最高 600M/s,隨機寫只有 100KB/s。RocketMQ 採用了零拷貝技術,提升了消息存盤和網絡發送的速度。

消息存儲結構

RocketMQ 消息的存儲是由 ConsumerQueue 和 CommitLog 配合完成的,消息真正的物理存儲文件是 CommitLog,ConsumerQueue 是消息的邏輯隊列,相似數據庫的索引文件,存儲指向物理存儲的地址。每一個 Topic 下的每一個 MessageQueue 都有一個對應的 ConsumerQueue 文件。

刷盤機制

RocketMQ 爲了提升性能,會盡量的保證磁盤的順序寫,消息在經過 Producer 寫入 RocketMQ 的時候,有兩種寫磁盤方式,分佈式同步刷盤和異步刷盤。

  • 同步刷盤

    在返回寫成功狀態時,消息當即被寫入磁盤。具體流程是,消息寫入內存的 PAGECACHE 後,馬上通知刷盤線程刷盤,而後等刷盤完成,刷盤線程執行完成後換行等待的線程,返回消息寫成功的狀態。

  • 異步刷盤

    在返回寫成功狀態時,消息可能只是被寫入了內存的 PAGECACHE,寫操做的返回快,吞吐量達,當內存裏的消息積累到必定程度時,統一出發寫磁盤動做,快速寫入。

高可用機制

RocketMQ 分佈式集羣是經過 Master 和 Slave 的配合達到高可用性的。

Master 角色的 Broker 支持讀和寫,Slave 角色的 Broker 僅支持讀,也就是 Producer 只能和 Master 角色的 Borker 鏈接寫入消息。Consumer 能夠鏈接 Master 角色的 broker,也能夠鏈接 Slave 角色的 Broker 來讀取消息。

消費端的高可用,默認先從 Master 讀,當 Master 角色出現故障後,Consumer 仍然能夠從 Slave 讀取消息,達到了消費端的高可用。

雙主從機制保證了發送消息的高可用。

消息主從複製

  • 同步複製

    同步複製方式是等 Master 和 Slave 均寫成功後才反饋給客戶端寫成功狀態。

    同步複製方式下,若是 Master 出故障,Slave 上有所有的備份數據,容易恢復,可是同步複製會增大數據寫入延遲,下降系統吞吐量。

  • 異步複製

    異步複製方式只要 Master 寫入成功便可反饋給客戶端寫入成功狀態。

    在異步複製方式下,系統擁有較低的延遲和較高的吞吐量,可是若是 Master 出了故障,有些數據由於沒有被寫入 Slave,又可能會丟失。

  • 通常把刷盤配置成異步的,主從之間同步配置成同步的,這樣即便有一臺機器出故障,仍然能保障數據不丟失,是個不錯的選擇。

負載均衡

Producer 端,每一個實力在發送消息的時候,默認會輪訓全部 message queue 發送,以達到讓消息平均落在不一樣的 queue 上,而因爲 queue 能夠散落在不一樣的 broker,因此消息就發送到不一樣的 broker 下。

Consumer 負載均衡

  • 集羣模式

    在集羣消費模式下,每條消息只須要投遞到訂閱這個 topic 的 Cosnuemr Group 下的一個實例便可。RocketMQ 採用主動拉取並消費消息,在拉取的時候須要明確指定拉取哪一條 message queue。


消息重試

  • 順序消息的重試

    對於順序消息,當消費者消費消息失敗後,消息隊列 RocketMQ 會自動不斷進行消息重試,這時,應用會出現消息消費被阻塞的狀況。所以,在使用順序消息時,無比保證應用可以及時監控並處理消費失敗的狀況,避免阻塞現象的發生。

  • 無序消息的重試

    對於無序消息(普通,定時,延時,事務消息),當消費消費消息失敗時,您能夠經過設置返回狀態達到消息重試的結果。

死信隊列

當一條消息消費失敗,消息隊列 RocketMQ 會自動進行消息重試,達到最大重試次數後,若消費依然失敗,則代表消費者在正常狀況下沒法正確地消費該消息,此時,消息隊列 RocketMQ 不會馬上將消息丟棄,而是發送到死信隊列中。

消費冪等

消息隊列 RocketMQ 消費者在接收到消息之後,有必要根據業務上的惟一 Key 對消息作冪等處理的必要性。互聯網應用中,尤爲是網絡不穩定狀況下,消息隊列 RocketMQ 的消息可能會出現重複:

  • 發送消息重複
  • 投遞消息重複
  • 負載均衡時消息重複

由於 Message ID 又可能出現衝突的狀況,因此真正的安全的冪等處理,不建議以 Message ID 做爲處理依據,最好方式是以業務惟一標識做爲冪等的關鍵依據,而業務的惟一標示能夠經過消息 Key 進行設置。

相關文章
相關標籤/搜索