十分鐘入門RocketMQ

塵央發表於2016-12-08

本文首先引出訊息中介軟體通常需要解決哪些問題,在解決這些問題當中會遇到什麼困難,Apache RocketMQ作為阿里開源的一款高效能、高吞吐量的分散式訊息中介軟體否可以解決,規範中如何定義這些問題。然後本文將介紹RocketMQ的架構設計,以期讓讀者快速瞭解RocketMQ。

一、訊息中介軟體需要解決哪些問題?

Publish/Subscribe

釋出訂閱是訊息中介軟體的最基本功能,也是相對於傳統RPC通訊而言。在此不再詳述。

Message Priority

規範中描述的優先順序是指在一個訊息佇列中,每條訊息都有不同的優先順序,一般用整數來描述,優先順序高的訊息先投遞,如果訊息完全在一個記憶體佇列中,那麼在投遞前可以按照優先順序排序,令優先順序高的先投遞。
由於RocketMQ所有訊息都是持久化的,所以如果按照優先順序來排序,開銷會非常大,因此RocketMQ沒有特意支援訊息優先順序,但是可以通過變通的方式實現類似功能,即單獨配置一個優先順序高的佇列,和一個普通優先順序的佇列, 將不同優先順序傳送到不同佇列即可。

對於優先順序問題,可以歸納為2類:

  1. 只要達到優先順序目的即可,不是嚴格意義上的優先順序,通常將優先順序劃分為高、中、低,或者再多幾個級別。每個優先順序可以用不同的topic表示,發訊息時,指定不同的topic來表示優先順序,這種方式可以解決絕大部分的優先順序問題,但是對業務的優先順序精確性做了妥協。
  2. 嚴格的優先順序,優先順序用整數表示,例如0 ~ 65535,這種優先順序問題一般使用不同topic解決就非常不合適。如果要讓MQ解決此問題,會對MQ的效能造成非常大的影響。這裡要確保一點,業務上是否確實需要這種嚴格的優先順序,如果將優先順序壓縮成幾個,對業務的影響有多大?

Message Order

訊息有序指的是一類訊息消費時,能按照傳送的順序來消費。例如:一個訂單產生了3條訊息,分別是訂單建立,訂單付款,訂單完成。消費時,要按照這個順序消費才能有意義。但是同時訂單之間是可以並行消費的。
RocketMQ可以嚴格的保證訊息有序。

Message Filter

Broker端訊息過濾

在Broker中,按照Consumer的要求做過濾,優點是減少了對於Consumer無用訊息的網路傳輸。
缺點是增加了Broker的負擔,實現相對複雜。

  1. 淘寶Notify支援多種過濾方式,包含直接按照訊息型別過濾,靈活的語法表示式過濾,幾乎可以滿足最苛刻的過濾需求。
  2. 淘寶RocketMQ支援按照簡單的Message Tag過濾,也支援按照Message Header、body進行過濾。
  3. CORBA Notification規範中也支援靈活的語法表示式過濾。

Consumer端訊息過濾

這種過濾方式可由應用完全自定義實現,但是缺點是很多無用的訊息要傳輸到Consumer端。

Message Persistence

訊息中介軟體通常採用的幾種持久化方式:

  1. 持久化到資料庫,例如Mysql。
  2. 持久化到KV儲存,例如levelDB、伯克利DB等KV儲存系統。
  3. 檔案記錄形式持久化,例如Kafka,RocketMQ
  4. 對記憶體資料做一個持久化映象,例如beanstalkd,VisiNotify
  5. (1)、(2)、(3)三種持久化方式都具有將記憶體佇列Buffer進行擴充套件的能力,(4)只是一個記憶體的映象,作用是當Broker掛掉重啟後仍然能將之前記憶體的資料恢復出來。

JMS與CORBA Notification規範沒有明確說明如何持久化,但是持久化部分的效能直接決定了整個訊息中介軟體的效能。

RocketMQ充分利用Linux檔案系統記憶體cache來提高效能。

Message Reliablity

影響訊息可靠性的幾種情況:

  1. Broker正常關閉
  2. Broker異常Crash
  3. OS Crash
  4. 機器掉電,但是能立即恢復供電情況。
  5. 機器無法開機(可能是cpu、主機板、記憶體等關鍵裝置損壞)
  6. 磁碟裝置損壞。

(1)、(2)、(3)、(4)四種情況都屬於硬體資源可立即恢復情況,RocketMQ在這四種情況下能保證訊息不丟,或者丟失少量資料(依賴刷盤方式是同步還是非同步)。

(5)、(6)屬於單點故障,且無法恢復,一旦發生,在此單點上的訊息全部丟失。RocketMQ在這兩種情況下,通過非同步複製,可保證99%的訊息不丟,但是仍然會有極少量的訊息可能丟失。通過同步雙寫技術可以完全避免單點,同步雙寫勢必會影響效能,適合對訊息可靠性要求極高的場合,例如與Money相關的應用。

RocketMQ從3.0版本開始支援同步雙寫。

Low Latency Messaging

在訊息不堆積情況下,訊息到達Broker後,能立刻到達Consumer。
RocketMQ使用長輪詢Pull方式,可保證訊息非常實時,訊息實時性不低於Push。

At least Once

是指每個訊息必須投遞一次。
RocketMQ Consumer先pull訊息到本地,消費完成後,才向伺服器返回ack,如果沒有消費一定不會ack訊息,所以RocketMQ可以很好的支援此特性。

Exactly Only Once

  1. 傳送訊息階段,不允許傳送重複的訊息。
  2. 消費訊息階段,不允許消費重複的訊息。

只有以上兩個條件都滿足情況下,才能認為訊息是“Exactly Only Once”,而要實現以上兩點,在分散式系統環境下,不可避免要產生巨大的開銷。所以RocketMQ為了追求高效能,並不保證此特性,要求在業務上進行去重,也就是說消費訊息要做到冪等性。RocketMQ雖然不能嚴格保證不重複,但是正常情況下很少會出現重複傳送、消費情況,只有網路異常,Consumer啟停等異常情況下會出現訊息重複。

此問題的本質原因是網路呼叫存在不確定性,即既不成功也不失敗的第三種狀態,所以才產生了訊息重複性問題。

Broker的Buffer滿了怎麼辦?

Broker的Buffer通常指的是Broker中一個佇列的記憶體Buffer大小,這類Buffer通常大小有限,如果Buffer滿了以後怎麼辦?
下面是CORBA Notification規範中處理方式:

  1. RejectNewEvents
    拒絕新來的訊息,向Producer返回RejectNewEvents錯誤碼。
  2. 按照特定策略丟棄已有訊息

    • AnyOrder – Any event may be discarded on overflow. This is the default setting for this
      property.
    • FifoOrder – The first event received will be the first discarded.
    • LifoOrder – The last event received will be the first discarded.
    • PriorityOrder – Events should be discarded in priority order, such that lower priority events will be discarded before higher priority events.
    • DeadlineOrder – Events should be discarded in the order of shortest expiry deadline first.

    RocketMQ沒有記憶體Buffer概念,RocketMQ的佇列都是持久化磁碟,資料定期清除。

對於此問題的解決思路,RocketMQ同其他MQ有非常顯著的區別,RocketMQ的記憶體Buffer抽象成一個無限長度的佇列,不管有多少資料進來都能裝得下,這個無限是有前提的,Broker會定期刪除過期的資料,例如Broker只儲存3天的訊息,那麼這個Buffer雖然長度無限,但是3天前的資料會被從隊尾刪除。

回溯消費

回溯消費是指Consumer已經消費成功的訊息,由於業務上需求需要重新消費,要支援此功能,Broker在向Consumer投遞成功訊息後,訊息仍然需要保留。並且重新消費一般是按照時間維度,例如由於Consumer系統故障,恢復後需要重新消費1小時前的資料,那麼Broker要提供一種機制,可以按照時間維度來回退消費進度。
RocketMQ支援按照時間回溯消費,時間維度精確到毫秒,可以向前回溯,也可以向後回溯。

訊息堆積

訊息中介軟體的主要功能是非同步解耦,還有個重要功能是擋住前端的資料洪峰,保證後端系統的穩定性,這就要求訊息中介軟體具有一定的訊息堆積能力,訊息堆積分以下兩種情況:

  1. 訊息堆積在記憶體Buffer,一旦超過記憶體Buffer,可以根據一定的丟棄策略來丟棄訊息,如CORBA Notification規範中描述。適合能容忍丟棄訊息的業務,這種情況訊息的堆積能力主要在於記憶體Buffer大小,而且訊息堆積後,效能下降不會太大,因為記憶體中資料多少對於對外提供的訪問能力影響有限。
  2. 訊息堆積到持久化儲存系統中,例如DB,KV儲存,檔案記錄形式。
    當訊息不能在記憶體Cache命中時,要不可避免的訪問磁碟,會產生大量讀IO,讀IO的吞吐量直接決定了訊息堆積後的訪問能力。

評估訊息堆積能力主要有以下四點:

  1. 訊息能堆積多少條,多少位元組?即訊息的堆積容量。
  2. 訊息堆積後,發訊息的吞吐量大小,是否會受堆積影響?
  3. 訊息堆積後,正常消費的Consumer是否會受影響?
  4. 訊息堆積後,訪問堆積在磁碟的訊息時,吞吐量有多大?

分散式事務

已知的幾個分散式事務規範,如XA,JTA等。其中XA規範被各大資料庫廠商廣泛支援,如Oracle,Mysql等。其中XA的TM實現佼佼者如Oracle Tuxedo,在金融、電信等領域被廣泛應用。

分散式事務涉及到兩階段提交問題,在資料儲存方面的方面必然需要KV儲存的支援,因為第二階段的提交回滾需要修改訊息狀態,一定涉及到根據Key去查詢Message的動作。RocketMQ在第二階段繞過了根據Key去查詢Message的問題,採用第一階段傳送Prepared訊息時,拿到了訊息的Offset,第二階段通過Offset去訪問訊息,並修改狀態,Offset就是資料的地址。

RocketMQ這種實現事務方式,沒有通過KV儲存做,而是通過Offset方式,存在一個顯著缺陷,即通過Offset更改資料,會令系統的髒頁過多,需要特別關注。

定時訊息

定時訊息是指訊息發到Broker後,不能立刻被Consumer消費,要到特定的時間點或者等待特定的時間後才能被消費。
如果要支援任意的時間精度,在Broker層面,必須要做訊息排序,如果再涉及到持久化,那麼訊息排序要不可避免的產生巨大效能開銷。
RocketMQ支援定時訊息,但是不支援任意時間精度,支援特定的level,例如定時5s,10s,1m等。

訊息重試

Consumer消費訊息失敗後,要提供一種重試機制,令訊息再消費一次。Consumer消費訊息失敗通常可以認為有以下幾種情況:

  1. 由於訊息本身的原因,例如反序列化失敗,訊息資料本身無法處理(例如話費充值,當前訊息的手機號被登出,無法充值)等。這種錯誤通常需要跳過這條訊息,再消費其他訊息,而這條失敗的訊息即使立刻重試消費,99%也不成功,所以最好提供一種定時重試機制,即過10s秒後再重試。
  2. 由於依賴的下游應用服務不可用,例如db連線不可用,外系統網路不可達等。遇到這種錯誤,即使跳過當前失敗的訊息,消費其他訊息同樣也會報錯。這種情況建議應用sleep 30s,再消費下一條訊息,這樣可以減輕Broker重試訊息的壓力。

RocketMQ Overview

RocketMQ是否解決了上述訊息中介軟體面臨的問題,接下來讓我們一探究竟。

RocketMQ 是什麼?

screenshot.png

上圖是一個典型的訊息中介軟體收發訊息的模型,RocketMQ也是這樣的設計,簡單說來,RocketMQ具有以下特點:

  • 是一個佇列模型的訊息中介軟體,具有高效能、高可靠、高實時、分散式特點。
  • Producer、Consumer、佇列都可以分散式。
  • Producer向一些佇列輪流傳送訊息,佇列集合稱為Topic,Consumer如果做廣播消費,則一個consumer例項消費這個Topic對應的所有佇列,如果做叢集消費,則多個Consumer例項平均消費這個topic對應的佇列集合。
  • 能夠保證嚴格的訊息順序
  • 提供豐富的訊息拉取模式
  • 高效的訂閱者水平擴充套件能力
  • 實時的訊息訂閱機制
  • 億級訊息堆積能力
  • 較少的依賴

RocketMQ 物理部署結構

screenshot.png

如上圖所示, RocketMQ的部署結構有以下特點:

  • Name Server是一個幾乎無狀態節點,可叢集部署,節點之間無任何資訊同步。
  • Broker部署相對複雜,Broker分為Master與Slave,一個Master可以對應多個Slave,但是一個Slave只能對應一個Master,Master與Slave的對應關係通過指定相同的BrokerName,不同的BrokerId來定義,BrokerId為0表示Master,非0表示Slave。Master也可以部署多個。每個Broker與Name Server叢集中的所有節點建立長連線,定時註冊Topic資訊到所有Name Server。
  • Producer與Name Server叢集中的其中一個節點(隨機選擇)建立長連線,定期從Name Server取Topic路由資訊,並向提供Topic服務的Master建立長連線,且定時向Master傳送心跳。Producer完全無狀態,可叢集部署。
  • Consumer與Name Server叢集中的其中一個節點(隨機選擇)建立長連線,定期從Name Server取Topic路由資訊,並向提供Topic服務的Master、Slave建立長連線,且定時向Master、Slave傳送心跳。Consumer既可以從Master訂閱訊息,也可以從Slave訂閱訊息,訂閱規則由Broker配置決定。

RocketMQ 邏輯部署結構

screenshot.png

如上圖所示,RocketMQ的邏輯部署結構有Producer和Consumer兩個特點。

  • Producer Group

用來表示一個傳送訊息應用,一個Producer Group下包含多個Producer例項,可以是多臺機器,也可以是一臺機器的多個程式,或者一個程式的多個Producer物件。一個Producer Group可以傳送多個Topic訊息,Producer Group作用如下:

  1. 標識一類Producer
  2. 可以通過運維工具查詢這個傳送訊息應用下有多個Producer例項
  3. 傳送分散式事務訊息時,如果Producer中途意外當機,Broker會主動回撥Producer Group內的任意一臺機器來確認事務狀態。
  • Consumer Group

用來表示一個消費訊息應用,一個Consumer Group下包含多個Consumer例項,可以是多臺機器,也可以是多個程式,或者是一個程式的多個Consumer物件。一個Consumer Group下的多個Consumer以均攤方式消費訊息,如果設定為廣播方式,那麼這個Consumer Group下的每個例項都消費全量資料。

RocketMQ 資料儲存結構

screenshot.png

如上圖所示,RocketMQ採取了一種資料與索引分離的儲存方法。有效降低檔案資源、IO資源,記憶體資源的損耗。即便是阿里這種海量資料,高併發場景也能夠有效降低端到端延遲,並具備較強的橫向擴充套件能力。


相關文章