阿里架構師加持,十分鐘入門RocketMQ,就是這麼簡單!

本文轉載自:阿里架構師加持,十分鐘入門RocketMQ,就是這麼簡單!


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

一、消息中間件需要解決哪些問題?

1. Publish/Subscribe

發佈訂閱是消息中間件的最基本功能,也是相對於傳統RPC通信而言。在此不再詳述。

2. Message Priority

規範中描述的優先級是指在一個消息隊列中,每條消息都有不同的優先級,一般用整數來描述,優先級高的消息先投遞,如果消息完全在一個內存隊列中,那麼在投遞前可以按照優先級排序,令優先級高的先投遞。

由於RocketMQ所有消息都是持久化的,所以如果按照優先級來排序,開銷會非常大,因此RocketMQ沒有特意支持消息優先級,但是可以通過變通的方式實現類似功能,即單獨配置一個優先級高的隊列,和一個普通優先級的隊列, 將不同優先級發送到不同隊列即可。

對於優先級問題,可以歸納爲2類:

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

3. Message Order

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

RocketMQ可以嚴格的保證消息有序。

4. Message Filter

①. Broker端消息過濾

在Broker中,按照Consumer的要求做過濾,優點是減少了對於Consumer無用消息的網絡傳輸。

缺點是增加了Broker的負擔,實現相對複雜。

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

②. Consumer端消息過濾

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

5. 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來提高性能。

6. 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版本開始支持同步雙寫。

7. Low Latency Messaging

在消息不堆積情況下,消息到達Broker後,能立刻到達Consumer。

RocketMQ使用長輪詢Pull方式,可保證消息非常實時,消息實時性不低於Push。

8. At least Once

是指每個消息必須投遞一次。

RocketMQ Consumer先pull消息到本地,消費完成後,才向服務器返回ack,如果沒有消費一定不會ack消息,所以RocketMQ可以很好的支持此特性。

9. Exactly Only Once

發送消息階段,不允許發送重複的消息。

消費消息階段,不允許消費重複的消息。

只有以上兩個條件都滿足情況下,才能認爲消息是“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消費消息失敗通常可以認爲有以下幾種情況:

由於消息本身的原因,例如反序列化失敗,消息數據本身無法處理(例如話費充值,當前消息的手機號被註銷,無法充值)等。這種錯誤通常需要跳過這條消息,再消費其他消息,而這條失敗的消息即使立刻重試消費,99%也不成功,所以最好提供一種定時重試機制,即過10s秒後再重試。

由於依賴的下游應用服務不可用,例如db連接不可用,外系統網絡不可達等。遇到這種錯誤,即使跳過當前失敗的消息,消費其他消息同樣也會報錯。這種情況建議應用sleep 30s,再消費下一條消息,這樣可以減輕Broker重試消息的壓力。

八、RocketMQ Overview

RocketMQ是否解決了上述消息中間件面臨的問題,接下來讓我們一探究竟。

1. RocketMQ 是什麼?

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

是一個隊列模型的消息中間件,具有高性能、高可靠、高實時、分佈式特點。

Producer、Consumer、隊列都可以分佈式。

Producer向一些隊列輪流發送消息,隊列集合稱爲Topic,Consumer如果做廣播消費,則一個consumer實例消費這個Topic對應的所有隊列,如果做集羣消費,則多個Consumer實例平均消費這個topic對應的隊列集合。

  1. 能夠保證嚴格的消息順序
  2. 提供豐富的消息拉取模式
  3. 高效的訂閱者水平擴展能力
  4. 實時的消息訂閱機制
  5. 億級消息堆積能力
  6. 較少的依賴

2. RocketMQ 物理部署結構

如上圖所示, 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配置決定。

3. RocketMQ 邏輯部署結構

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

①. Producer Group

用來表示一個發送消息應用,一個Producer Group下包含多個Producer實例,可以是多臺機器,也可以是一臺機器的多個進程,或者一個進程的多個Producer對象。一個Producer Group可以發送多個Topic消息,Producer Group作用如下:

標識一類Producer

可以通過運維工具查詢這個發送消息應用下有多個Producer實例

發送分佈式事務消息時,如果Producer中途意外宕機,Broker會主動回調Producer Group內的任意一臺機器來確認事務狀態。

②. Consumer Group

用來表示一個消費消息應用,一個Consumer Group下包含多個Consumer實例,可以是多臺機器,也可以是多個進程,或者是一個進程的多個Consumer對象。一個Consumer Group下的多個Consumer以均攤方式消費消息,如果設置爲廣播方式,那麼這個Consumer Group下的每個實例都消費全量數據。

4. RocketMQ 數據存儲結構

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


本文轉載自:阿里架構師加持,十分鐘入門RocketMQ,就是這麼簡單!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章