?【Alibaba中介軟體技術系列】「RocketMQ技術專題」幫你梳理RocketMQ或Kafka的選擇理由以及二者PK

浩宇天尚發表於2021-11-12

前提背景

大家都知道,市面上有許多開源的MQ,例如,RocketMQ、Kafka、RabbitMQ等等,現在Pulsar也開始發光,今天我們談談筆者最常用的RocketMQ和Kafka,想必大家早就知道二者之間的特點以及區別,但是在實際場景中,二者的選取有可能會範迷惑,那麼今天筆者就帶領大家分析一下二者之間的區別,以及選取標準吧!

架構對比

RocketMQ的架構

RocketMQ由NameServer、Broker、Consumer、Producer組成,NameServer之間互不通訊,Broker會向所有的nameServer註冊,通過心跳判斷broker是否存活,producer和consumer 通過nameserver就知道broker上有哪些topic。

Kafka的架構

Kafka的後設資料資訊都是儲存在Zookeeper,新版本部分已經存放到了Kafka內部了,由Broker、Zookeeper、Producer、Consumer組成。

Broker對比

主從架構模型差異:

維度不同
  • Kafka的master/slave是基於partition(分割槽)維度的,而RocketMQ是基於Broker維度的;

    • Kafka的master/slave是可以切換的(主要依靠於Zookeeper的主備切換機制)
    • RocketMQ無法實現自動切換,當RocketMQ的Master當機時,讀能被路由到slave上,但寫會被路由到此topic的其他Broker上。
刷盤機制

RocketMQ支援同步刷盤,也就是每次訊息都等刷入磁碟後再返回,保證訊息不丟失,但對吞吐量稍有影響。一般在主從結構下,選擇非同步雙寫策略是比較可靠的選擇。

訊息查詢

RocketMQ支援訊息查詢,除了queue的offset外,還支援自定義key。RocketMQ對offset和key都做了索引,均是獨立的索引檔案。

消費失敗重試與延遲消費

RocketMQ針對每個topic都定義了延遲佇列,當訊息消費失敗時,會發回給Broker存入延遲佇列中,每個消費者在啟動時預設訂閱延遲佇列,這樣消費失敗的訊息在一段時候後又能夠重新消費。

  • 延遲時間與延遲級別一一對應,延遲時間是隨失敗次數逐漸增加的,最後一次間隔2小時。

  • 當然傳送訊息是也可以指定延遲級別,這樣就能主動設定延遲消費,在一些特定場景下還是有作用的。

資料讀寫速度
  • Kafka每個partition獨佔一個目錄,每個partition均有各自的資料檔案.log,相當於一個topic有多個log檔案。

  • RocketMQ是每個topic共享一個資料檔案commitlog,

Kafka的topic一般有多個partition,所以Kafka的資料寫入速度比RocketMQ高出一個量級。

但Kafka的分割槽數超過一定數量的檔案同時寫入,會導致原先的順序寫轉為隨機寫,效能急劇下降,所以kafka的分割槽數量是有限制的。

隨機和順序讀寫的對比

  • 連續 / 隨機 I/O(在底層硬碟維度)

    • 連續 I/O :指的是本次 I/O 給出的初始扇區地址和上一次 I/O 的結束扇區地址是完全連續或者相隔不多的。反之,如果相差很大,則算作一次隨機 I/O。
  • 發生隨機I/O可能是因為磁碟碎片導致磁碟空間不連續,或者當前block空間小於檔案大小導致的。

連續 I/O 比隨機 I/O 效率高的原因是
  • 連續 I/O,磁頭幾乎不用換道,或者換道的時間很短;
  • 隨機 I/O,如果這個 I/O 很多的話,會導致磁頭不停地換道,造成效率的極大降低。
隨機和順序速度比較

IOPS和吞吐量:為何隨機是關注IOPS,順序關注吞吐量?

  • 隨機在每次IO操作的定址時間和旋轉延時都不能忽略不計,而這兩個時間的存在也就限制了IOPS的大小;

  • 順序讀寫可以忽略不計定址時間和旋轉延時,主要花費在資料傳輸的時間上。

IOPS來衡量一個IO系統效能的時候,要說明讀寫的方式以及單次IO的大小,因為讀寫方式會受到旋轉時間和尋道時間影響,而單次IO會受到資料傳輸時間影響。

服務治理
  • Kafka用Zookeeper來做服務發現和治理,broker和consumer都會向其註冊自身資訊,同時訂閱相應的znode,這樣當有broker或者consumer當機時能立刻感知,做相應的調整;

  • RocketMQ用自定義的nameServer做服務發現和治理,其實時性差點,比如如果broker當機,producer和consumer不會實時感知到,需要等到下次更新broker叢集時(最長30S)才能做相應調整,服務有個不可用的視窗期,但資料不會丟失,且能保證一致性。

    • 但是某個consumer當機,broker會實時反饋給其他consumer,立即觸發負載均衡,這樣能一定程度上保證訊息消費的實時性。

Producer差異

傳送方式
  • kafka預設使用非同步傳送的形式,有一個memory buffer暫存訊息,同時會將多個訊息整合成一個資料包傳送,這樣能提高吞吐量,但對訊息的實效有些影響

  • RocketMQ可選擇使用同步或者非同步傳送。

傳送響應

Kafka的傳送ack支援三種設定:

  • 訊息存進memory buffer就返回(0);

  • 等到leader收到訊息返回(1)

  • 等到leader和isr的follower都收到訊息返回(-1)

上面也介紹了,Kafka都是非同步刷盤

RocketMQ都需要等broker的響應確認,有同步刷盤,非同步刷盤,同步雙寫,非同步雙寫等策略,相比於Kafka多了一個同步刷盤

Consumer差異

訊息過濾
  • RocketMQ的queue和kafka的partition對應,但RocketMQ的topic還能更加細分,可對訊息加tag,同時訂閱時也可指定特定的tag來對訊息做更進一步的過濾。
有序訊息
  • RocketMQ支援全域性有序和區域性有序

  • Kafka也支援有序訊息,但是如果某個broker當機了,就不能在保證有序了。

消費確認

RocketMQ僅支援手動確認,也就是消費完一條訊息ack+1,會定期向broker同步消費進度,或者在下一次pull時附帶上offset。

Kafka支援定時確認,拉取到訊息自動確認和手動確認,offset存在zookeeper上。

消費並行度

Kafka的消費者預設是單執行緒的,一個Consumer可以訂閱一個或者多個Partition,一個Partition同一時間只能被一個消費者消費,也就是有多少個Partition就最多有多少個執行緒同時消費。

如分割槽數為10,那麼最多10臺機器來並行消費(每臺機器只能開啟一個執行緒),或者一臺機器消費(10個執行緒並行消費)。即消費並行度和分割槽數一致。

RocketMQ消費並行度分兩種情況:有序消費模式和併發消費模式,

  • 有序模式下,一個消費者也只存在一個執行緒消費,並行度同Kafka完全一致。

  • 併發模式下,每次拉取的訊息按consumeMessageBatchMaxSize(預設1)拆分後分配給消費者執行緒池,消費者執行緒池min=20,max=64。也就是每個queue的併發度在20-64之間,一個topic有多個queue就相乘。所以rocketmq的併發度比Kafka高出一個量級

併發消費方式並行度取決於Consumer的執行緒數,如Topic配置10個佇列,10臺機器消費,每臺機器100個執行緒,那麼並行度為1000。

事務訊息

RocketMQ指定一定程度上的事務訊息,當前開源版本刪除了事務訊息回查功能,事務機制稍微變得沒有這麼可靠了,不過阿里雲的rocketmq支援可靠的事務訊息;kafka不支援分散式事務訊息。

Topic和Tag的區別?

業務是否相關聯

  • 無直接關聯的訊息:淘寶交易訊息,京東物流訊息使用不同的 Topic 進行區分。

  • 交易訊息,電器類訂單、女裝類訂單、化妝品類訂單的訊息可以用Tag進行區分。

訊息優先順序是否一致:如同樣是物流訊息,盒馬必須小時內送達,天貓超市 24 小時內送達,淘寶物流則相對會慢一些,不同優先順序的訊息用不同的 Topic 進行區分。

訊息量級是否相當:有些業務訊息雖然量小但是實時性要求高,如果跟某些萬億量級的訊息使用同一個Topic,則有可能會因為過長的等待時間而“餓死”,此時需要將不同量級的訊息進行拆分,使用不同的Topic。

Tag和Topic的選用

針對訊息分類,您可以選擇建立多個Topic,或者在同一個Topic下建立多個Tag。

不同的Topic之間的訊息沒有必然的聯絡。

Tag則用來區分同一個Topic下相互關聯的訊息,例如全集和子集的關係、流程先後的關係。

通過合理的使用 Topic 和 Tag,可以讓業務結構清晰,更可以提高效率。

Tag怎麼實現訊息過濾

RocketMQ分散式訊息佇列的訊息過濾方式有別於其它MQ中介軟體,是在Consumer端訂閱訊息時再做訊息過濾的。

RocketMQ這麼做是在於其Producer端寫入訊息和Consumer端訂閱訊息採用分離儲存的機制來實現的,Consumer端訂閱訊息是需要通過ConsumeQueue這個訊息消費的邏輯佇列拿到一個索引,然後再從CommitLog裡面讀取真正的訊息實體內容,所以說到底也是還繞不開其儲存結構。

ConsumeQueue的儲存結構:可以看到其中有8個位元組儲存的Message Tag的雜湊值,基於Tag的訊息過濾是基於這個欄位值的。

Tag過濾方式
  • Consumer端在訂閱訊息時除了指定Topic還可以指定Tag,如果一個訊息有多個Tag,可以用||分隔。

  • Consumer端會將這個訂閱請求構建成一個SubscriptionData,傳送一個Pull訊息的請求給Broker端。

  • Broker端從RocketMQ的檔案儲存層—Store讀取資料之前,會用這些資料先構建一個MessageFilter,然後傳給Store。

  • Store從ConsumeQueue讀取到一條記錄後,會用它記錄的訊息tag hash值去做過濾,由於在服務端只是根據hashcode進行判斷。

無法精確對tag原始字串進行過濾,故在訊息消費端拉取到訊息後,還需要對訊息的原始tag字串進行比對,如果不同,則丟棄該訊息,不進行訊息消費。

Message Body過濾方式

向伺服器上傳一段Java程式碼,可以對訊息做任意形式的過濾,甚至可以做Message Body的過濾拆分

資料訊息的堆積能力

理論上Kafka要比RocketMQ的堆積能力更強,不過RocketMQ單機也可以支援億級的訊息堆積能力,我們認為這個堆積能力已經完全可以滿足業務需求。

訊息資料回溯

  • Kafka理論上可以按照Offset來回溯訊息

  • RocketMQ支援按照時間來回溯訊息,精度毫秒,例如從一天之前的某時某分某秒開始重新消費訊息,典型業務場景如consumer做訂單分析,但是由於程式邏輯或者依賴的系統發生故障等原因,導致今天消費的訊息全部無效,需要重新從昨天零點開始消費,那麼以時間為起點的訊息重放功能對於業務非常有幫助。

效能對比

  • Kafka單機寫入TPS約在百萬條/秒,訊息大小10個位元組

  • RocketMQ單機寫入TPS單例項約7萬條/秒,單機部署3個Broker,可以跑到最高12萬條/秒,訊息大小10個位元組。

資料一致性和實時性

訊息投遞實時性

  • Kafka使用短輪詢方式,實時性取決於輪詢間隔時間

  • RocketMQ使用長輪詢,同Push方式實時性一致,訊息的投遞延時通常在幾個毫秒。

消費失敗重試

  • Kafka消費失敗不支援重試

  • RocketMQ消費失敗支援定時重試,每次重試間隔時間順延

訊息順序

  • Kafka支援訊息順序,但是一臺Broker當機後,就會產生訊息亂序

  • RocketMQ支援嚴格的訊息順序,在順序訊息場景下,一臺Broker當機後,傳送訊息會失敗,但是不會亂序

Mysql Binlog分發需要嚴格的訊息順序

(題外話)Kafka沒有的,RocketMQ獨有的tag機制

普通訊息、事務訊息、定時(延時)訊息、順序訊息,不同的訊息型別使用不同的 Topic,無法通過Tag進行區分。

總結

  • RocketMQ定位於非日誌的可靠訊息傳輸(日誌場景也OK),目前RocketMQ在阿里集團被廣泛應用在訂單,交易,充值,流計算,訊息推送,日誌流式處理,binglog分發等場景。

  • RocketMQ的同步刷盤在單機可靠性上比Kafka更高,不會因為作業系統Crash,導致資料丟失。

  • 同時同步Replication也比Kafka非同步Replication更可靠,資料完全無單點。

  • 另外Kafka的Replication以topic為單位,支援主機當機,備機自動切換,但是這裡有個問題,由於是非同步Replication,那麼切換後會有資料丟失,同時Leader如果重啟後,會與已經存在的Leader產生資料衝突。

  • 例如充值類應用,當前時刻呼叫運營商閘道器,充值失敗,可能是對方壓力過多,稍後在呼叫就會成功,如支付寶到銀行扣款也是類似需求。這裡的重試需要可靠的重試,即失敗重試的訊息不因為Consumer當機導致丟失。

相關文章