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

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)

分享創造快樂