https://www.zhihu.com/question/43557507java
https://baijiahao.baidu.com/s?id=1610644333184173190&wfr=spider&for=pc算法
本文將從,Kafka、RabbitMQ、ZeroMQ、RocketMQ、ActiveMQ 17 個方面綜合對比做爲消息隊列使用時的差別。數據庫
1、資料文檔緩存
Kafka:中。有kafka做者本身寫的書,網上資料也有一些。服務器
rabbitmq:多。有一些不錯的書,網上資料多。網絡
zeromq:少。沒有專門寫zeromq的書,網上的資料可能是一些代碼的實現和簡單介紹。多線程
rocketmq:少。沒有專門寫rocketmq的書,網上的資料參差不齊,官方文檔很簡潔,可是對技術細節沒有過多的描述。架構
activemq:多。沒有專門寫activemq的書,網上資料多。併發
2、開發語言負載均衡
Kafka:Scala
rabbitmq:Erlang
zeromq:c
rocketmq:java
activemq:java
3、支持的協議
Kafka:本身定義的一套...(基於TCP)
rabbitmq:AMQP
zeromq:TCP、UDP
rocketmq:本身定義的一套...
activemq:OpenWire、STOMP、REST、XMPP、AMQP
4、消息存儲
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:內存、磁盤、數據庫。支持少許堆積。
5、消息事務
Kafka:支持
rabbitmq:支持。
客戶端將信道設置爲事務模式,只有當消息被rabbitMq接收,事務才能提交成功,不然在捕獲異常後進行回滾。使用事務會使得性能有所降低
zeromq:不支持
rocketmq:支持
activemq:支持
6、負載均衡
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實現負載均衡。
7、集羣方式
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:支持簡單集羣模式,好比'主-備',對高級集羣模式支持很差。
8、管理界面
Kafka:通常
rabbitmq:好
zeromq:無
rocketmq:無
activemq:通常
9、可用性
Kafka:很是高(分佈式)
rabbitmq:高(主從)
zeromq:高。
rocketmq:很是高(分佈式)
activemq:高(主從)
10、消息重複
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
11、吞吐量TPS
Kafka:極大
Kafka按批次發送消息和消費消息。發送端將多個小消息合併,批量發向Broker,消費端每次取出一個批次的消息批量處理。
rabbitmq:比較大
zeromq:極大
rocketmq:大
rocketMQ接收端能夠批量消費消息,能夠配置每次消費的消息數,可是發送端不是批量發送。
activemq:比較大
12、訂閱形式和消息分發
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綁定的隊列
headers>與key無關,消息內容的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的消息。
十3、順序消息
Kafka:支持。
設置生產者的max.in.flight.requests.per.connection爲1,能夠保證消息是按照發送順序寫入服務器的,即便發生了重試。
kafka保證同一個分區裏的消息是有序的,可是這種有序分兩種狀況
1>key爲null,消息逐個被寫入不一樣主機的分區中,可是對於每一個分區依然是有序的
2>key不爲null , 消息被寫入到同一個分區,這個分區的消息都是有序。
rabbitmq:不支持
zeromq:不支持
rocketmq:支持
activemq:不支持
十4、消息確認
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:支持。
十5、消息回溯
Kafka:支持指定分區offset位置的回溯。
rabbitmq:不支持
zeromq:不支持
rocketmq:支持指定時間點的回溯。
activemq:不支持
十6、消息重試
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 秒後再重試。
2. 因爲依賴的下游應用服務不可用,例如 db 鏈接不可用,外系統網絡不可達等。
即便跳過當前失敗的消息,消費其餘消息一樣也會報錯。這種狀況能夠 sleep 30s,再消費下一條消息,減輕 Broker 重試消息的壓力。
activemq:不支持
十7、併發度
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:高