歡迎光臨
每天分享高質量文章

17 個方面,綜合對比 Kafka、RabbitMQ、RocketMQ、ActiveMQ 四個分散式訊息佇列

  • 一、資料檔案
  • 二、開發語言
  • 三、支援的協議
  • 四、訊息儲存
  • 五、訊息事務
  • 六、負載均衡
  • 七、叢集方式
  • 八、管理介面
  • 九、可用性
  • 十、訊息重覆
  • 十一、吞吐量TPS
  • 十二、訂閱形式和訊息分發
  • 十三、順序訊息
  • 十四、訊息確認
  • 十五、訊息回溯
  • 十六、訊息重試
  • 十七、併發度

本文將從,Kafka、RabbitMQ、ZeroMQ、RocketMQ、ActiveMQ 17 個方面綜合對比作為訊息佇列使用時的差異。

一、資料檔案

Kafka:中。有kafka作者自己寫的書,網上資料也有一些。 rabbitmq:多。有一些不錯的書,網上資料多。 zeromq:少。沒有專門寫zeromq的書,網上的資料多是一些程式碼的實現和簡單介紹。 rocketmq:少。沒有專門寫rocketmq的書,網上的資料良莠不齊,官方檔案很簡潔,但是對技術細節沒有過多的描述。 activemq:多。沒有專門寫activemq的書,網上資料多。

二、開發語言

Kafka:Scala rabbitmq:Erlang zeromq:c rocketmq:java activemq:java

三、支援的協議

Kafka:自己定義的一套…(基於TCP) rabbitmq:AMQP zeromq:TCP、UDP rocketmq:自己定義的一套… activemq:OpenWire、STOMP、REST、XMPP、AMQP

四、訊息儲存

Kafka:記憶體、磁碟、資料庫。支援大量堆積。

kafka的最小儲存單元是分割槽,一個topic包含多個分割槽,kafka建立主題時,這些分割槽會被分配在多個伺服器上,通常一個broker一臺伺服器。 分割槽首領會均勻地分佈在不同的伺服器上,分割槽副本也會均勻的分佈在不同的伺服器上,確保負載均衡和高可用性,當新的broker加入叢集的時候,部分副本會被移動到新的broker上。 根據配置檔案中的目錄清單,kafka會把新的分割槽分配給目錄清單裡分割槽數最少的目錄。 預設情況下,分割槽器使用輪詢演演算法把訊息均衡地分佈在同一個主題的不同分割槽中,對於傳送時指定了key的情況,會根據key的hashcode取模後的值存到對應的分割槽中。

rabbitmq:記憶體、磁碟。支援少量堆積。

rabbitmq的訊息分為持久化的訊息和非持久化訊息,不管是持久化的訊息還是非持久化的訊息都可以寫入到磁碟。 持久化的訊息在到達佇列時就寫入到磁碟,並且如果可以,持久化的訊息也會在記憶體中儲存一份備份,這樣可以提高一定的效能,當記憶體吃緊的時候會從記憶體中清除。非持久化的訊息一般只存在於記憶體中,在記憶體吃緊的時候會被換入到磁碟中,以節省記憶體。

引入映象佇列機制,可將重要佇列“複製”到叢集中的其他broker上,保證這些佇列的訊息不會丟失。配置映象的佇列,都包含一個主節點master和多個從節點slave,如果master失效,加入時間最長的slave會被提升為新的master,除傳送訊息外的所有動作都向master傳送,然後由master將命令執行結果廣播給各個slave,rabbitmq會讓master均勻地分佈在不同的伺服器上,而同一個佇列的slave也會均勻地分佈在不同的伺服器上,保證負載均衡和高可用性。

zeromq:訊息傳送端的記憶體或者磁碟中。不支援持久化。

rocketmq:磁碟。支援大量堆積。

commitLog檔案存放實際的訊息資料,每個commitLog上限是1G,滿了之後會自動新建一個commitLog檔案儲存資料。ConsumeQueue佇列只存放offset、size、tagcode,非常小,分佈在多個broker上。ConsumeQueue相當於CommitLog的索引檔案,消費者消費時會從consumeQueue中查詢訊息在commitLog中的offset,再去commitLog中查詢元資料。

ConsumeQueue儲存格式的特性,保證了寫過程的順序寫盤(寫CommitLog檔案),大量資料IO都在順序寫同一個commitLog,滿1G了再寫新的。加上rocketmq是累計4K才強制從PageCache中刷到磁碟(快取),所以高併發寫效能突出。

activemq:記憶體、磁碟、資料庫。支援少量堆積。

五、訊息事務

Kafka:支援 rabbitmq:支援。 客戶端將通道設定為事務樣式,只有當訊息被rabbitMq接收,事務才能提交成功,否則在捕獲異常後進行回滾。使用事務會使得效能有所下降 zeromq:不支援 rocketmq:支援 activemq:支援

六、負載均衡

Kafka:支援負載均衡。

1>一個broker通常就是一臺伺服器節點。對於同一個Topic的不同分割槽,Kafka會儘力將這些分割槽分佈到不同的Broker伺服器上,zookeeper儲存了broker、主題和分割槽的元資料資訊。分割槽首領會處理來自客戶端的生產請求,kafka分割槽首領會被分配到不同的broker伺服器上,讓不同的broker伺服器共同分擔任務。

每一個broker都快取了元資料資訊,客戶端可以從任意一個broker獲取元資料資訊並快取起來,根據元資料資訊知道要往哪裡傳送請求。

2>kafka的消費者組訂閱同一個topic,會盡可能地使得每一個消費者分配到相同數量的分割槽,分攤負載。

3>當消費者加入或者退出消費者組的時候,還會觸發再均衡,為每一個消費者重新分配分割槽,分攤負載。

kafka的負載均衡大部分是自動完成的,分割槽的建立也是kafka完成的,隱藏了很多細節,避免了繁瑣的配置和人為疏忽造成的負載問題。

4>傳送端由topic和key來決定訊息發往哪個分割槽,如果key為null,那麼會使用輪詢演演算法將訊息均衡地傳送到同一個topic的不同分割槽中。如果key不為null,那麼會根據key的hashcode取模計算出要發往的分割槽。

rabbitmq:對負載均衡的支援不好。

1>訊息被投遞到哪個佇列是由交換器和key決定的,交換器、路由鍵、佇列都需要手動建立。

rabbitmq客戶端傳送訊息要和broker建立連線,需要事先知道broker上有哪些交換器,有哪些佇列。通常要宣告要傳送的標的佇列,如果沒有標的佇列,會在broker上建立一個佇列,如果有,就什麼都不處理,接著往這個佇列傳送訊息。假設大部分繁重任務的佇列都建立在同一個broker上,那麼這個broker的負載就會過大。(可以在上線前預先建立佇列,無需宣告要傳送的佇列,但是傳送時不會嘗試建立佇列,可能出現找不到佇列的問題,rabbitmq的備份交換器會把找不到佇列的訊息儲存到一個專門的佇列中,以便以後查詢使用)

使用映象佇列機制建立rabbitmq叢集可以解決這個問題,形成master-slave的架構,master節點會均勻分佈在不同的伺服器上,讓每一臺伺服器分攤負載。slave節點只是負責轉發,在master失效時會選擇加入時間最長的slave成為master。

當新節點加入映象佇列的時候,佇列中的訊息不會同步到新的slave中,除非呼叫同步命令,但是呼叫命令後,佇列會阻塞,不能在生產環境中呼叫同步命令。

2>當rabbitmq佇列擁有多個消費者的時候,佇列收到的訊息將以輪詢的分發方式傳送給消費者。每條訊息只會傳送給訂閱串列裡的一個消費者,不會重覆。

這種方式非常適合擴充套件,而且是專門為併發程式設計的。

如果某些消費者的任務比較繁重,那麼可以設定basicQos限制通道上消費者能保持的最大未確認訊息的數量,在達到上限時,rabbitmq不再向這個消費者傳送任何訊息。

3>對於rabbitmq而言,客戶端與叢集建立的TCP連線不是與叢集中所有的節點建立連線,而是挑選其中一個節點建立連線。

但是rabbitmq叢集可以藉助HAProxy、LVS技術,或者在客戶端使用演演算法實現負載均衡,引入負載均衡之後,各個客戶端的連線可以分攤到叢集的各個節點之中。

客戶端均衡演演算法:

1)輪詢法。按順序傳回下一個伺服器的連線地址。

2)加權輪詢法。給配置高、負載低的機器配置更高的權重,讓其處理更多的請求;而配置低、負載高的機器,給其分配較低的權重,降低其系統負載。

3)隨機法。隨機選取一個伺服器的連線地址。

4)加權隨機法。按照機率隨機選取連線地址。

5)源地址雜湊法。透過雜湊函式計算得到的一個數值,用該數值對伺服器串列的大小進行取模運算。

6)最小連線數法。動態選擇當前連線數最少的一臺伺服器的連線地址。

zeromq:去中心化,不支援負載均衡。本身只是一個多執行緒網路庫。

rocketmq:支援負載均衡。

一個broker通常是一個伺服器節點,broker分為master和slave,master和slave儲存的資料一樣,slave從master同步資料。

1>nameserver與每個叢集成員保持心跳,儲存著Topic-Broker路由資訊,同一個topic的佇列會分佈在不同的伺服器上。

2>傳送訊息透過輪詢佇列的方式傳送,每個佇列接收平均的訊息量。傳送訊息指定topic、tags、keys,無法指定投遞到哪個佇列(沒有意義,叢集消費和廣播消費跟訊息存放在哪個佇列沒有關係)。

tags選填,類似於 Gmail 為每封郵件設定的標簽,方便伺服器過濾使用。目前只支 持每個訊息設定一個 tag,所以也可以類比為 Notify 的 MessageType 概念。

keys選填,代表這條訊息的業務關鍵詞,伺服器會根據 keys 建立雜湊索引,設定後, 可以在 Console 系統根據 Topic、Keys 來查詢訊息,由於是雜湊索引,請盡可能 保證 key 唯一,例如訂單號,商品 Id 等。

3>rocketmq的負載均衡策略規定:Consumer數量應該小於等於Queue數量,如果Consumer超過Queue數量,那麼多餘的Consumer 將不能消費訊息。這一點和kafka是一致的,rocketmq會盡可能地為每一個Consumer分配相同數量的佇列,分攤負載。

activemq:支援負載均衡。可以基於zookeeper實現負載均衡。

七、叢集方式

Kafka:天然的‘Leader-Slave’無狀態叢集,每臺伺服器既是Master也是Slave。

分割槽首領均勻地分佈在不同的kafka伺服器上,分割槽副本也均勻地分佈在不同的kafka伺服器上,所以每一臺kafka伺服器既含有分割槽首領,同時又含有分割槽副本,每一臺kafka伺服器是某一臺kafka伺服器的Slave,同時也是某一臺kafka伺服器的leader。

kafka的叢集依賴於zookeeper,zookeeper支援熱擴充套件,所有的broker、消費者、分割槽都可以動態加入移除,而無需關閉服務,與不依靠zookeeper叢集的mq相比,這是最大的優勢。

rabbitmq:支援簡單叢集,’複製’樣式,對高階叢集樣式支援不好。

rabbitmq的每一個節點,不管是單一節點系統或者是叢集中的一部分,要麼是記憶體節點,要麼是磁碟節點,叢集中至少要有一個是磁碟節點。

在rabbitmq叢集中建立佇列,叢集只會在單個節點建立佇列行程和完整的佇列資訊(元資料、狀態、內容),而不是在所有節點上建立。

引入映象佇列,可以避免單點故障,確保服務的可用性,但是需要人為地為某些重要的佇列配置映象。

zeromq:去中心化,不支援叢集。

rocketmq:常用 多對’Master-Slave’ 樣式,開源版本需手動切換Slave變成Master

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配置決定。

客戶端先找到NameServer, 然後透過NameServer再找到 Broker。

一個topic有多個佇列,這些佇列會均勻地分佈在不同的broker伺服器上。rocketmq佇列的概念和kafka的分割槽概念是基本一致的,kafka同一個topic的分割槽盡可能地分佈在不同的broker上,分割槽副本也會分佈在不同的broker上。

rocketmq叢集的slave會從master拉取資料備份,master分佈在不同的broker上。

activemq:支援簡單叢集樣式,比如’主-備’,對高階叢集樣式支援不好。

八、管理介面

Kafka:一般 rabbitmq:好 zeromq:無 rocketmq:無 activemq:一般

九、可用性

Kafka:非常高(分散式) rabbitmq:高(主從) zeromq:高。 rocketmq:非常高(分散式) activemq:高(主從)

十、訊息重覆

Kafka:支援at least once、at most once

rabbitmq:支援at least once、at most once

zeromq:只有重傳機制,但是沒有持久化,訊息丟了重傳也沒有用。既不是at least once、也不是at most once、更不是exactly only once

rocketmq:支援at least once

activemq:支援at least once

十一、吞吐量TPS

Kafka:極大 Kafka按批次傳送訊息和消費訊息。傳送端將多個小訊息合併,批次發向Broker,消費端每次取出一個批次的訊息批次處理。 rabbitmq:比較大 zeromq:極大 rocketmq:大 rocketMQ接收端可以批次消費訊息,可以配置每次消費的訊息數,但是傳送端不是批次傳送。 activemq:比較大

十二、訂閱形式和訊息分發

Kafka:基於topic以及按照topic進行正則匹配的釋出訂閱樣式。

【傳送】

傳送端由topic和key來決定訊息發往哪個分割槽,如果key為null,那麼會使用輪詢演演算法將訊息均衡地傳送到同一個topic的不同分割槽中。如果key不為null,那麼會根據key的hashcode取模計算出要發往的分割槽。

【接收】

1>consumer向群組協調器broker傳送心跳來維持他們和群組的從屬關係以及他們對分割槽的所有權關係,所有權關係一旦被分配就不會改變除非發生再均衡(比如有一個consumer加入或者離開consumer group),consumer只會從對應的分割槽讀取訊息。

2>kafka限制consumer個數要少於分割槽個數,每個訊息只會被同一個 Consumer Group的一個consumer消費(非廣播)。

3>kafka的 Consumer Group訂閱同一個topic,會盡可能地使得每一個consumer分配到相同數量的分割槽,不同 Consumer Group訂閱同一個主題相互獨立,同一個訊息會被不同的 Consumer Group處理。

rabbitmq:提供了4種:direct, topic ,Headers和fanout。

【傳送】

先要宣告一個佇列,這個佇列會被建立或者已經被建立,佇列是基本儲存單元。

由exchange和key決定訊息儲存在哪個佇列。

direct>傳送到和bindingKey完全匹配的佇列。

topic>路由key是含有”.”的字串,會傳送到含有“*”、“#”進行模糊匹配的bingKey對應的佇列。

fanout>與key無關,會傳送到所有和exchange系結的佇列

essay-headers>與key無關,訊息內容的essay-headers屬性(一個鍵值對)和系結鍵值對完全匹配時,會傳送到此佇列。此方式效能低一般不用

【接收】

rabbitmq的佇列是基本儲存單元,不再被分割槽或者分片,對於我們已經建立了的佇列,消費端要指定從哪一個佇列接收訊息。

當rabbitmq佇列擁有多個消費者的時候,佇列收到的訊息將以輪詢的分發方式傳送給消費者。每條訊息只會傳送給訂閱串列裡的一個消費者,不會重覆。

這種方式非常適合擴充套件,而且是專門為併發程式設計的。

如果某些消費者的任務比較繁重,那麼可以設定basicQos限制通道上消費者能保持的最大未確認訊息的數量,在達到上限時,rabbitmq不再向這個消費者傳送任何訊息。

zeromq:點對點(p2p)

rocketmq:基於topic/messageTag以及按照訊息型別、屬性進行正則匹配的釋出訂閱樣式

【傳送】

傳送訊息透過輪詢佇列的方式傳送,每個佇列接收平均的訊息量。傳送訊息指定topic、tags、keys,無法指定投遞到哪個佇列(沒有意義,叢集消費和廣播消費跟訊息存放在哪個佇列沒有關係)。

tags選填,類似於 Gmail 為每封郵件設定的標簽,方便伺服器過濾使用。目前只支 持每個訊息設定一個 tag,所以也可以類比為 Notify 的 MessageType 概念。

keys選填,代表這條訊息的業務關鍵詞,伺服器會根據 keys 建立雜湊索引,設定後, 可以在 Console 系統根據 Topic、Keys 來查詢訊息,由於是雜湊索引,請盡可能 保證 key 唯一,例如訂單號,商品 Id 等。

【接收】

1>廣播消費。一條訊息被多個Consumer消費,即使Consumer屬於同一個ConsumerGroup,訊息也會被ConsumerGroup中的每個Consumer都消費一次。

2>叢集消費。一個 Consumer Group中的Consumer實體平均分攤消費訊息。例如某個Topic有 9 條訊息,其中一個Consumer Group有3個實體,那麼每個實體只消費其中的 3 條訊息。即每一個佇列都把訊息輪流分發給每個consumer。

activemq:點對點(p2p)、廣播(釋出-訂閱)

點對點樣式,每個訊息只有1個消費者;

釋出/訂閱樣式,每個訊息可以有多個消費者。

【傳送】

點對點樣式:先要指定一個佇列,這個佇列會被建立或者已經被建立。

釋出/訂閱樣式:先要指定一個topic,這個topic會被建立或者已經被建立。

【接收】

點對點樣式:對於已經建立了的佇列,消費端要指定從哪一個佇列接收訊息。

釋出/訂閱樣式:對於已經建立了的topic,消費端要指定訂閱哪一個topic的訊息。

十三、順序訊息

Kafka:支援。

設定生產者的max.in.flight.requests.per.connection為1,可以保證訊息是按照傳送順序寫入伺服器的,即使發生了重試。

kafka保證同一個分割槽裡的訊息是有序的,但是這種有序分兩種情況

1>key為null,訊息逐個被寫入不同主機的分割槽中,但是對於每個分割槽依然是有序的

2>key不為null , 訊息被寫入到同一個分割槽,這個分割槽的訊息都是有序。

rabbitmq:不支援

zeromq:不支援

rocketmq:支援

activemq:不支援

十四、訊息確認

Kafka:支援。

1>傳送方確認機制

ack=0,不管訊息是否成功寫入分割槽

ack=1,訊息成功寫入首領分割槽後,傳回成功

ack=all,訊息成功寫入所有分割槽後,傳回成功。

2>接收方確認機制

自動或者手動提交分割槽偏移量,早期版本的kafka偏移量是提交給Zookeeper的,這樣使得zookeeper的壓力比較大,更新版本的kafka的偏移量是提交給kafka伺服器的,不再依賴於zookeeper群組,叢集的效能更加穩定。

rabbitmq:支援。

1>傳送方確認機制,訊息被投遞到所有匹配的佇列後,傳回成功。如果訊息和佇列是可持久化的,那麼在寫入磁碟後,傳回成功。支援批次確認和非同步確認。

2>接收方確認機制,設定autoAck為false,需要顯式確認,設定autoAck為true,自動確認。

當autoAck為false的時候,rabbitmq佇列會分成兩部分,一部分是等待投遞給consumer的訊息,一部分是已經投遞但是沒收到確認的訊息。如果一直沒有收到確認訊號,並且consumer已經斷開連線,rabbitmq會安排這個訊息重新進入佇列,投遞給原來的消費者或者下一個消費者。

未確認的訊息不會有過期時間,如果一直沒有確認,並且沒有斷開連線,rabbitmq會一直等待,rabbitmq允許一條訊息處理的時間可以很久很久。

zeromq:支援。

rocketmq:支援。

activemq:支援。

十五、訊息回溯

Kafka:支援指定分割槽offset位置的回溯。 rabbitmq:不支援 zeromq:不支援 rocketmq:支援指定時間點的回溯。 activemq:不支援

十六、訊息重試

Kafka:不支援,但是可以實現。

kafka支援指定分割槽offset位置的回溯,可以實現訊息重試。

rabbitmq:不支援,但是可以利用訊息確認機制實現。

rabbitmq接收方確認機制,設定autoAck為false。

當autoAck為false的時候,rabbitmq佇列會分成兩部分,一部分是等待投遞給consumer的訊息,一部分是已經投遞但是沒收到確認的訊息。如果一直沒有收到確認訊號,並且consumer已經斷開連線,rabbitmq會安排這個訊息重新進入佇列,投遞給原來的消費者或者下一個消費者。

zeromq:不支援,

rocketmq:支援。

訊息消費失敗的大部分場景下,立即重試99%都會失敗,所以rocketmq的策略是在消費失敗時定時重試,每次時間間隔相同。

1>傳送端的 send 方法本身支援內部重試,重試邏輯如下:

a)至多重試3次;

b)如果傳送失敗,則輪轉到下一個broker;

c)這個方法的總耗時不超過sendMsgTimeout 設定的值,預設 10s,超過時間不在重試。

2>接收端。

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

  1. 由於訊息本身的原因,例如反序列化失敗,訊息資料本身無法處理(例如話費充值,當前訊息的手機號被

登出,無法充值)等。定時重試機制,比如過 10s 秒後再重試。

  1. 由於依賴的下游應用服務不可用,例如 db 連線不可用,外系統網路不可達等。

即使跳過當前失敗的訊息,消費其他訊息同樣也會報錯。這種情況可以 sleep 30s,再消費下一條訊息,減輕 Broker 重試訊息的壓力。

activemq:不支援

十七、併發度

Kafka:高

一個執行緒一個消費者,kafka限制消費者的個數要小於等於分割槽數,如果要提高並行度,可以在消費者中再開啟多執行緒,或者增加consumer實體數量。

rabbitmq:極高

本身是用Erlang語言寫的,併發效能高。

可在消費者中開啟多執行緒,最常用的做法是一個channel對應一個消費者,每一個執行緒把持一個channel,多個執行緒復用connection的tcp連線,減少效能開銷。

當rabbitmq佇列擁有多個消費者的時候,佇列收到的訊息將以輪詢的分發方式傳送給消費者。每條訊息只會傳送給訂閱串列裡的一個消費者,不會重覆。

這種方式非常適合擴充套件,而且是專門為併發程式設計的。

如果某些消費者的任務比較繁重,那麼可以設定basicQos限制通道上消費者能保持的最大未確認訊息的數量,在達到上限時,rabbitmq不再向這個消費者傳送任何訊息。

zeromq:高

rocketmq:高

1>rocketmq限制消費者的個數少於等於佇列數,但是可以在消費者中再開啟多執行緒,這一點和kafka是一致的,提高並行度的方法相同。

修改消費並行度方法

a) 同一個 ConsumerGroup 下,透過增加 Consumer 實體數量來提高並行度,超過訂閱佇列數的 Consumer實體無效。

b) 提高單個 Consumer 的消費並行執行緒,透過修改引數consumeThreadMin、consumeThreadMax

2>同一個網路連線connection,客戶端多個執行緒可以同時傳送請求,連線會被覆用,減少效能開銷。

activemq:高

單個ActiveMQ的接收和消費訊息的速度在1萬筆/秒(持久化 一般為1-2萬, 非持久化 2 萬以上),在生產環境中部署10個Activemq就能達到10萬筆/秒以上的效能,部署越多的activemq broker 在MQ上latency也就越低,系統吞吐量也就越高。

已同步到看一看
贊(0)

分享創造快樂