1、引言
對(duì)于即時(shí)通訊網(wǎng)來說,所有的技術(shù)文章和資料都在圍繞即時(shí)通訊這個(gè)技術(shù)方向進(jìn)行整理和分享,這一次也不例外。對(duì)于即時(shí)通訊系統(tǒng)(包括IM、消息推送系統(tǒng)等)來說,MQ消息中件間是非常常見的基礎(chǔ)軟件,但市面上種類眾多、各有所長的MQ消息中件間產(chǎn)品,該怎么去選擇?這是個(gè)問題!
對(duì)于很多經(jīng)驗(yàn)不足的開發(fā)者來說,一個(gè)公司內(nèi)部用的IM聊天系統(tǒng),總用戶量也不過百十來人,動(dòng)輒就是Kafka、MongoDB,美其名曰為了高性能和可擴(kuò)展性,真是大炮打蚊子。而對(duì)于中大型的即時(shí)通訊場景來說,有的開發(fā)者確為了貪圖使用簡單、資料全面,反而使用臃腫不堪的ActiveMQ,這就有點(diǎn)失去章法了。
唧唧歪歪這么多,那什么樣的場景到底該用哪種MQ消息中件間產(chǎn)品合適?讀完本文您或許就有了答案。
本文將從17個(gè)維度綜合對(duì)比Kafka、RabbitMQ、ZeroMQ、RocketMQ、ActiveMQ這5款當(dāng)前最主流的MQ消息中間件產(chǎn)品,希望能為您的下一次產(chǎn)品的架構(gòu)設(shè)計(jì)和MQ消息中間件選型提供參考依據(jù)。
學(xué)習(xí)交流:
- 即時(shí)通訊/推送技術(shù)開發(fā)交流4群:101279154[推薦]
- 移動(dòng)端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動(dòng)端IM》
(本文同步發(fā)布于:http://www.52im.net/thread-2625-1-1.html)
2、相關(guān)資料
官網(wǎng)地址:相關(guān)文章:3、維度1:資料文檔
1)Kafka:資料數(shù)量中等。有Kafka作者自己寫的書,網(wǎng)上資料也有一些。
2)RabbitMQ:資料數(shù)量多。有一些不錯(cuò)的書,網(wǎng)上資料多。
3)ZeroMQ:資料數(shù)量少。專門寫ZeroMQ的書較少,網(wǎng)上的資料多是一些代碼的實(shí)現(xiàn)和簡單介紹。
4)RocketMQ:資料數(shù)量少。專門寫RocketMQ的書目前有了兩本;網(wǎng)上的資料良莠不齊,官方文檔很簡潔,但是對(duì)技術(shù)細(xì)節(jié)沒有過多的描述。
5)ActiveMQ:資料數(shù)量多。沒有專門寫ActiveMQ的書,網(wǎng)上資料多。
4、維度2:開發(fā)語言
1)Kafka:Scala
2)RabbitMQ:Erlang
3)ZeroMQ:C語言
4)RocketMQ:Java
5)ActiveMQ:Java
5、維度3:支持的協(xié)議
1)Kafka:自己定義的一套…(基于TCP)
2)RabbitMQ:AMQP
3)ZeroMQ:TCP、UDP
4)RocketMQ:自己定義的一套…
5)ActiveMQ:OpenWire、STOMP、REST、XMPP、AMQP
6、維度4:消息存儲(chǔ)
1)Kafka:
內(nèi)存、磁盤、數(shù)據(jù)庫。支持大量堆積。
Kafka的最小存儲(chǔ)單元是分區(qū),一個(gè)topic包含多個(gè)分區(qū),Kafka創(chuàng)建主題時(shí),這些分區(qū)會(huì)被分配在多個(gè)服務(wù)器上,通常一個(gè)broker一臺(tái)服務(wù)器。
分區(qū)首領(lǐng)會(huì)均勻地分布在不同的服務(wù)器上,分區(qū)副本也會(huì)均勻的分布在不同的服務(wù)器上,確保負(fù)載均衡和高可用性,當(dāng)新的broker加入集群的時(shí)候,部分副本會(huì)被移動(dòng)到新的broker上。
根據(jù)配置文件中的目錄清單,Kafka會(huì)把新的分區(qū)分配給目錄清單里分區(qū)數(shù)最少的目錄。
默認(rèn)情況下,分區(qū)器使用輪詢算法把消息均衡地分布在同一個(gè)主題的不同分區(qū)中,對(duì)于發(fā)送時(shí)指定了key的情況,會(huì)根據(jù)key的hashcode取模后的值存到對(duì)應(yīng)的分區(qū)中。
2)RabbitMQ:
內(nèi)存、磁盤。支持少量堆積。
RabbitMQ的消息分為持久化的消息和非持久化消息,不管是持久化的消息還是非持久化的消息都可以寫入到磁盤。
持久化的消息在到達(dá)隊(duì)列時(shí)就寫入到磁盤,并且如果可以,持久化的消息也會(huì)在內(nèi)存中保存一份備份,這樣可以提高一定的性能,當(dāng)內(nèi)存吃緊的時(shí)候會(huì)從內(nèi)存中清除。
非持久化的消息一般只存在于內(nèi)存中,在內(nèi)存吃緊的時(shí)候會(huì)被換入到磁盤中,以節(jié)省內(nèi)存。
引入鏡像隊(duì)列機(jī)制,可將重要隊(duì)列“復(fù)制”到集群中的其他broker上,保證這些隊(duì)列的消息不會(huì)丟失。
配置鏡像的隊(duì)列,都包含一個(gè)主節(jié)點(diǎn)master和多個(gè)從節(jié)點(diǎn)slave,如果master失效,加入時(shí)間最長的slave會(huì)被提升為新的master,除發(fā)送消息外的所有動(dòng)作都向master發(fā)送,然后由master將命令執(zhí)行結(jié)果廣播給各個(gè)slave,RabbitMQ會(huì)讓master均勻地分布在不同的服務(wù)器上,而同一個(gè)隊(duì)列的slave也會(huì)均勻地分布在不同的服務(wù)器上,保證負(fù)載均衡和高可用性。
3)ZeroMQ:
消息發(fā)送端的內(nèi)存或者磁盤中。不支持持久化。
4)RocketMQ:
磁盤。支持大量堆積。
commitLog文件存放實(shí)際的消息數(shù)據(jù),每個(gè)commitLog上限是1G,滿了之后會(huì)自動(dòng)新建一個(gè)commitLog文件保存數(shù)據(jù)。
ConsumeQueue隊(duì)列只存放offset、size、tagcode,非常小,分布在多個(gè)broker上。
ConsumeQueue相當(dāng)于CommitLog的索引文件,消費(fèi)者消費(fèi)時(shí)會(huì)從consumeQueue中查找消息在commitLog中的offset,再去commitLog中查找元數(shù)據(jù)。
ConsumeQueue存儲(chǔ)格式的特性,保證了寫過程的順序?qū)懕P(寫CommitLog文件),大量數(shù)據(jù)IO都在順序?qū)懲粋€(gè)commitLog,滿1G了再寫新的。
加上RocketMQ是累計(jì)4K才強(qiáng)制從PageCache中刷到磁盤(緩存),所以高并發(fā)寫性能突出。
5)ActiveMQ:
內(nèi)存、磁盤、數(shù)據(jù)庫。支持少量堆積。
7、維度5:消息事務(wù)
1)Kafka:支持
2)RabbitMQ:支持。客戶端將信道設(shè)置為事務(wù)模式,只有當(dāng)消息被RabbitMQ接收,事務(wù)才能提交成功,否則在捕獲異常后進(jìn)行回滾。使用事務(wù)會(huì)使得性能有所下降
3)ZeroMQ:不支持
4)RocketMQ:支持
5)ActiveMQ:支持
8、維度6:負(fù)載均衡
8.1 Kafka
支持負(fù)載均衡。
1)一個(gè)broker通常就是一臺(tái)服務(wù)器節(jié)點(diǎn)。
對(duì)于同一個(gè)Topic的不同分區(qū),Kafka會(huì)盡力將這些分區(qū)分布到不同的Broker服務(wù)器上,zookeeper保存了broker、主題和分區(qū)的元數(shù)據(jù)信息。
分區(qū)首領(lǐng)會(huì)處理來自客戶端的生產(chǎn)請(qǐng)求,Kafka分區(qū)首領(lǐng)會(huì)被分配到不同的broker服務(wù)器上,讓不同的broker服務(wù)器共同分擔(dān)任務(wù)。
每一個(gè)broker都緩存了元數(shù)據(jù)信息,客戶端可以從任意一個(gè)broker獲取元數(shù)據(jù)信息并緩存起來,根據(jù)元數(shù)據(jù)信息知道要往哪里發(fā)送請(qǐng)求。
2)Kafka的消費(fèi)者組訂閱同一個(gè)topic,會(huì)盡可能地使得每一個(gè)消費(fèi)者分配到相同數(shù)量的分區(qū),分?jǐn)傌?fù)載。
3)當(dāng)消費(fèi)者加入或者退出消費(fèi)者組的時(shí)候,還會(huì)觸發(fā)再均衡,為每一個(gè)消費(fèi)者重新分配分區(qū),分?jǐn)傌?fù)載。
Kafka的負(fù)載均衡大部分是自動(dòng)完成的,分區(qū)的創(chuàng)建也是Kafka完成的,隱藏了很多細(xì)節(jié),避免了繁瑣的配置和人為疏忽造成的負(fù)載問題。
4)發(fā)送端由topic和key來決定消息發(fā)往哪個(gè)分區(qū),如果key為null,那么會(huì)使用輪詢算法將消息均衡地發(fā)送到同一個(gè)topic的不同分區(qū)中。如果key不為null,那么會(huì)根據(jù)key的hashcode取模計(jì)算出要發(fā)往的分區(qū)。
8.2 RabbitMQ
對(duì)負(fù)載均衡的支持不好。
1)消息被投遞到哪個(gè)隊(duì)列是由交換器和key決定的,交換器、路由鍵、隊(duì)列都需要手動(dòng)創(chuàng)建。
RabbitMQ客戶端發(fā)送消息要和broker建立連接,需要事先知道broker上有哪些交換器,有哪些隊(duì)列。
通常要聲明要發(fā)送的目標(biāo)隊(duì)列,如果沒有目標(biāo)隊(duì)列,會(huì)在broker上創(chuàng)建一個(gè)隊(duì)列,如果有,就什么都不處理,接著往這個(gè)隊(duì)列發(fā)送消息。假設(shè)大部分繁重任務(wù)的隊(duì)列都創(chuàng)建在同一個(gè)broker上,那么這個(gè)broker的負(fù)載就會(huì)過大。
可以在上線前預(yù)先創(chuàng)建隊(duì)列,無需聲明要發(fā)送的隊(duì)列,但是發(fā)送時(shí)不會(huì)嘗試創(chuàng)建隊(duì)列,可能出現(xiàn)找不到隊(duì)列的問題,RabbitMQ的備份交換器會(huì)把找不到隊(duì)列的消息保存到一個(gè)專門的隊(duì)列中,以便以后查詢使用。
使用鏡像隊(duì)列機(jī)制建立RabbitMQ集群可以解決這個(gè)問題,形成master-slave的架構(gòu),master節(jié)點(diǎn)會(huì)均勻分布在不同的服務(wù)器上,讓每一臺(tái)服務(wù)器分?jǐn)傌?fù)載。slave節(jié)點(diǎn)只是負(fù)責(zé)轉(zhuǎn)發(fā),在master失效時(shí)會(huì)選擇加入時(shí)間最長的slave成為master。
當(dāng)新節(jié)點(diǎn)加入鏡像隊(duì)列的時(shí)候,隊(duì)列中的消息不會(huì)同步到新的slave中,除非調(diào)用同步命令,但是調(diào)用命令后,隊(duì)列會(huì)阻塞,不能在生產(chǎn)環(huán)境中調(diào)用同步命令。
2)當(dāng)RabbitMQ隊(duì)列擁有多個(gè)消費(fèi)者的時(shí)候,隊(duì)列收到的消息將以輪詢的分發(fā)方式發(fā)送給消費(fèi)者。每條消息只會(huì)發(fā)送給訂閱列表里的一個(gè)消費(fèi)者,不會(huì)重復(fù)。
這種方式非常適合擴(kuò)展,而且是專門為并發(fā)程序設(shè)計(jì)的。
如果某些消費(fèi)者的任務(wù)比較繁重,那么可以設(shè)置basicQos限制信道上消費(fèi)者能保持的最大未確認(rèn)消息的數(shù)量,在達(dá)到上限時(shí),RabbitMQ不再向這個(gè)消費(fèi)者發(fā)送任何消息。
3)對(duì)于RabbitMQ而言,客戶端與集群建立的TCP連接不是與集群中所有的節(jié)點(diǎn)建立連接,而是挑選其中一個(gè)節(jié)點(diǎn)建立連接。
但是RabbitMQ集群可以借助HAProxy、LVS技術(shù),或者在客戶端使用算法實(shí)現(xiàn)負(fù)載均衡,引入負(fù)載均衡之后,各個(gè)客戶端的連接可以分?jǐn)偟郊旱母鱾€(gè)節(jié)點(diǎn)之中。
客戶端均衡算法:
a. 輪詢法:按順序返回下一個(gè)服務(wù)器的連接地址。
b. 加權(quán)輪詢法:給配置高、負(fù)載低的機(jī)器配置更高的權(quán)重,讓其處理更多的請(qǐng)求;而配置低、負(fù)載高的機(jī)器,給其分配較低的權(quán)重,降低其系統(tǒng)負(fù)載。
c. 隨機(jī)法:隨機(jī)選取一個(gè)服務(wù)器的連接地址。
d. 加權(quán)隨機(jī)法:按照概率隨機(jī)選取連接地址。
e. 源地址哈希法:通過哈希函數(shù)計(jì)算得到的一個(gè)數(shù)值,用該數(shù)值對(duì)服務(wù)器列表的大小進(jìn)行取模運(yùn)算。
f. 最小連接數(shù)法:動(dòng)態(tài)選擇當(dāng)前連接數(shù)最少的一臺(tái)服務(wù)器的連接地址。
8.3 ZeroMQ
去中心化,不支持負(fù)載均衡。本身只是一個(gè)多線程網(wǎng)絡(luò)庫。
8.4 RocketMQ
支持負(fù)載均衡。
一個(gè)broker通常是一個(gè)服務(wù)器節(jié)點(diǎn),broker分為master和slave,master和slave存儲(chǔ)的數(shù)據(jù)一樣,slave從master同步數(shù)據(jù)。
1)nameserver與每個(gè)集群成員保持心跳,保存著Topic-Broker路由信息,同一個(gè)topic的隊(duì)列會(huì)分布在不同的服務(wù)器上。
2)發(fā)送消息通過輪詢隊(duì)列的方式發(fā)送,每個(gè)隊(duì)列接收平均的消息量。發(fā)送消息指定topic、tags、keys,無法指定投遞到哪個(gè)隊(duì)列(沒有意義,集群消費(fèi)和廣播消費(fèi)跟消息存放在哪個(gè)隊(duì)列沒有關(guān)系)。
tags選填,類似于 Gmail 為每封郵件設(shè)置的標(biāo)簽,方便服務(wù)器過濾使用。目前只支 持每個(gè)消息設(shè)置一個(gè) tag,所以也可以類比為 Notify 的 MessageType 概念。
keys選填,代表這條消息的業(yè)務(wù)關(guān)鍵詞,服務(wù)器會(huì)根據(jù) keys 創(chuàng)建哈希索引,設(shè)置后, 可以在 Console 系統(tǒng)根據(jù) Topic、Keys 來查詢消息,由于是哈希索引,請(qǐng)盡可能 保證 key 唯一,例如訂單號(hào),商品 Id 等。
3)RocketMQ的負(fù)載均衡策略規(guī)定:
Consumer數(shù)量應(yīng)該小于等于Queue數(shù)量,如果Consumer超過Queue數(shù)量,那么多余的Consumer 將不能消費(fèi)消息。
這一點(diǎn)和Kafka是一致的,RocketMQ會(huì)盡可能地為每一個(gè)Consumer分配相同數(shù)量的隊(duì)列,分?jǐn)傌?fù)載。
8.5 ActiveMQ
支持負(fù)載均衡。可以基于zookeeper實(shí)現(xiàn)負(fù)載均衡。
9、維度7:集群方式
1)Kafka:
天然的‘Leader-Slave’無狀態(tài)集群,每臺(tái)服務(wù)器既是Master也是Slave。
分區(qū)首領(lǐng)均勻地分布在不同的Kafka服務(wù)器上,分區(qū)副本也均勻地分布在不同的Kafka服務(wù)器上,所以每一臺(tái)Kafka服務(wù)器既含有分區(qū)首領(lǐng),同時(shí)又含有分區(qū)副本。
每一臺(tái)Kafka服務(wù)器是某一臺(tái)Kafka服務(wù)器的Slave,同時(shí)也是某一臺(tái)Kafka服務(wù)器的leader。
Kafka的集群依賴于zookeeper,zookeeper支持熱擴(kuò)展,所有的broker、消費(fèi)者、分區(qū)都可以動(dòng)態(tài)加入移除,而無需關(guān)閉服務(wù),與不依靠zookeeper集群的mq相比,這是最大的優(yōu)勢。
2)RabbitMQ:
支持簡單集群,'復(fù)制'模式,對(duì)高級(jí)集群模式支持不好。
RabbitMQ的每一個(gè)節(jié)點(diǎn),不管是單一節(jié)點(diǎn)系統(tǒng)或者是集群中的一部分,要么是內(nèi)存節(jié)點(diǎn),要么是磁盤節(jié)點(diǎn),集群中至少要有一個(gè)是磁盤節(jié)點(diǎn)。
在RabbitMQ集群中創(chuàng)建隊(duì)列,集群只會(huì)在單個(gè)節(jié)點(diǎn)創(chuàng)建隊(duì)列進(jìn)程和完整的隊(duì)列信息(元數(shù)據(jù)、狀態(tài)、內(nèi)容),而不是在所有節(jié)點(diǎn)上創(chuàng)建。
引入鏡像隊(duì)列,可以避免單點(diǎn)故障,確保服務(wù)的可用性,但是需要人為地為某些重要的隊(duì)列配置鏡像。
3)ZeroMQ:
去中心化,不支持集群。
4)RocketMQ:
常用 多對(duì)'Master-Slave' 模式,開源版本需手動(dòng)切換Slave變成Master。
Name Server是一個(gè)幾乎無狀態(tài)節(jié)點(diǎn),可集群部署,節(jié)點(diǎn)之間無任何信息同步。
Broker部署相對(duì)復(fù)雜,Broker分為Master與Slave,一個(gè)Master可以對(duì)應(yīng)多個(gè)Slave,但是一個(gè)Slave只能對(duì)應(yīng)一個(gè)Master。
Master與Slave的對(duì)應(yīng)關(guān)系通過指定相同的BrokerName,不同的BrokerId來定義,BrokerId為0表示Master,非0表示Slave。
Master也可以部署多個(gè)。每個(gè)Broker與Name Server集群中的所有節(jié)點(diǎn)建立長連接,定時(shí)注冊Topic信息到所有Name Server。
Producer與Name Server集群中的其中一個(gè)節(jié)點(diǎn)(隨機(jī)選擇)建立長連接,定期從Name Server取Topic路由信息,并向提供Topic服務(wù)的Master建立長連接,且定時(shí)向Master發(fā)送心跳。Producer完全無狀態(tài),可集群部署。
Consumer與Name Server集群中的其中一個(gè)節(jié)點(diǎn)(隨機(jī)選擇)建立長連接,定期從Name Server取Topic路由信息,并向提供Topic服務(wù)的Master、Slave建立長連接,且定時(shí)向Master、Slave發(fā)送心跳。
Consumer既可以從Master訂閱消息,也可以從Slave訂閱消息,訂閱規(guī)則由Broker配置決定。
客戶端先找到NameServer, 然后通過NameServer再找到 Broker。
一個(gè)topic有多個(gè)隊(duì)列,這些隊(duì)列會(huì)均勻地分布在不同的broker服務(wù)器上。RocketMQ隊(duì)列的概念和Kafka的分區(qū)概念是基本一致的,Kafka同一個(gè)topic的分區(qū)盡可能地分布在不同的broker上,分區(qū)副本也會(huì)分布在不同的broker上。
RocketMQ集群的slave會(huì)從master拉取數(shù)據(jù)備份,master分布在不同的broker上。
5)ActiveMQ:
支持簡單集群模式,比如'主-備',對(duì)高級(jí)集群模式支持不好。
10、維度8:管理界面
1)Kafka:一般
2)RabbitMQ:好
3)ZeroMQ:無
4)RocketMQ:有管理后臺(tái), 但不是項(xiàng)目里自帶, 需要自己啟動(dòng)一個(gè)單獨(dú)的管理后臺(tái)實(shí)例
5)ActiveMQ:一般
11、維度9:可用性
1)Kafka:非常高(分布式)
2)RabbitMQ:高(主從)
3)ZeroMQ:高
4)RocketMQ:非常高(分布式)
5)ActiveMQ:高(主從)
12、維度10:消息重復(fù)
1)Kafka:支持at least once、at most once
2)RabbitMQ:支持at least once、at most once
3)ZeroMQ:只有重傳機(jī)制,但是沒有持久化,消息丟了重傳也沒有用。既不是at least once、也不是at most once、更不是exactly only once
4)RocketMQ:支持at least once
5)ActiveMQ:支持at least once
13、維度11:吞吐量TPS
1)Kafka:極大。Kafka按批次發(fā)送消息和消費(fèi)消息。發(fā)送端將多個(gè)小消息合并,批量發(fā)向Broker,消費(fèi)端每次取出一個(gè)批次的消息批量處理。
2)RabbitMQ:比較大
3)ZeroMQ:極大
4)RocketMQ:大。RocketMQ接收端可以批量消費(fèi)消息,可以配置每次消費(fèi)的消息數(shù),但是發(fā)送端不是批量發(fā)送。
5)ActiveMQ:比較大
14、維度12:訂閱形式和消息分發(fā)
14.1 Kafka
基于topic以及按照topic進(jìn)行正則匹配的發(fā)布訂閱模式。
1)發(fā)送:
發(fā)送端由topic和key來決定消息發(fā)往哪個(gè)分區(qū),如果key為null,那么會(huì)使用輪詢算法將消息均衡地發(fā)送到同一個(gè)topic的不同分區(qū)中。如果key不為null,那么會(huì)根據(jù)key的hashcode取模計(jì)算出要發(fā)往的分區(qū)。
2)接收:
consumer向群組協(xié)調(diào)器broker發(fā)送心跳來維持他們和群組的從屬關(guān)系以及他們對(duì)分區(qū)的所有權(quán)關(guān)系,所有權(quán)關(guān)系一旦被分配就不會(huì)改變除非發(fā)生再均衡(比如有一個(gè)consumer加入或者離開consumer group),consumer只會(huì)從對(duì)應(yīng)的分區(qū)讀取消息。
Kafka限制consumer個(gè)數(shù)要少于分區(qū)個(gè)數(shù),每個(gè)消息只會(huì)被同一個(gè) Consumer Group的一個(gè)consumer消費(fèi)(非廣播)。
Kafka的 Consumer Group訂閱同一個(gè)topic,會(huì)盡可能地使得每一個(gè)consumer分配到相同數(shù)量的分區(qū),不同 Consumer Group訂閱同一個(gè)主題相互獨(dú)立,同一個(gè)消息會(huì)被不同的 Consumer Group處理。
14.2 RabbitMQ
提供了4種:direct、topic、Headers和fanout。
1)發(fā)送:
先要聲明一個(gè)隊(duì)列,這個(gè)隊(duì)列會(huì)被創(chuàng)建或者已經(jīng)被創(chuàng)建,隊(duì)列是基本存儲(chǔ)單元。
由exchange和key決定消息存儲(chǔ)在哪個(gè)隊(duì)列。
direct>發(fā)送到和bindingKey完全匹配的隊(duì)列。
topic>路由key是含有"."的字符串,會(huì)發(fā)送到含有“*”、“#”進(jìn)行模糊匹配的bingKey對(duì)應(yīng)的隊(duì)列。
fanout>與key無關(guān),會(huì)發(fā)送到所有和exchange綁定的隊(duì)列
headers>與key無關(guān),消息內(nèi)容的headers屬性(一個(gè)鍵值對(duì))和綁定鍵值對(duì)完全匹配時(shí),會(huì)發(fā)送到此隊(duì)列。此方式性能低一般不用
2)接收:
RabbitMQ的隊(duì)列是基本存儲(chǔ)單元,不再被分區(qū)或者分片,對(duì)于我們已經(jīng)創(chuàng)建了的隊(duì)列,消費(fèi)端要指定從哪一個(gè)隊(duì)列接收消息。
當(dāng)RabbitMQ隊(duì)列擁有多個(gè)消費(fèi)者的時(shí)候,隊(duì)列收到的消息將以輪詢的分發(fā)方式發(fā)送給消費(fèi)者。每條消息只會(huì)發(fā)送給訂閱列表里的一個(gè)消費(fèi)者,不會(huì)重復(fù)。
這種方式非常適合擴(kuò)展,而且是專門為并發(fā)程序設(shè)計(jì)的。
如果某些消費(fèi)者的任務(wù)比較繁重,那么可以設(shè)置basicQos限制信道上消費(fèi)者能保持的最大未確認(rèn)消息的數(shù)量,在達(dá)到上限時(shí),RabbitMQ不再向這個(gè)消費(fèi)者發(fā)送任何消息。
14.3 ZeroMQ
點(diǎn)對(duì)點(diǎn)(p2p)。
14.4 RocketMQ
基于topic/messageTag以及按照消息類型、屬性進(jìn)行正則匹配的發(fā)布訂閱模式。
1)發(fā)送:
發(fā)送消息通過輪詢隊(duì)列的方式發(fā)送,每個(gè)隊(duì)列接收平均的消息量。發(fā)送消息指定topic、tags、keys,無法指定投遞到哪個(gè)隊(duì)列(沒有意義,集群消費(fèi)和廣播消費(fèi)跟消息存放在哪個(gè)隊(duì)列沒有關(guān)系)。
tags選填,類似于 Gmail 為每封郵件設(shè)置的標(biāo)簽,方便服務(wù)器過濾使用。目前只支 持每個(gè)消息設(shè)置一個(gè) tag,所以也可以類比為 Notify 的 MessageType 概念。
keys選填,代表這條消息的業(yè)務(wù)關(guān)鍵詞,服務(wù)器會(huì)根據(jù) keys 創(chuàng)建哈希索引,設(shè)置后, 可以在 Console 系統(tǒng)根據(jù) Topic、Keys 來查詢消息,由于是哈希索引,請(qǐng)盡可能 保證 key 唯一,例如訂單號(hào),商品 Id 等。
2)接收:
廣播消費(fèi)。一條消息被多個(gè)Consumer消費(fèi),即使Consumer屬于同一個(gè)ConsumerGroup,消息也會(huì)被ConsumerGroup中的每個(gè)Consumer都消費(fèi)一次。
集群消費(fèi)。一個(gè) Consumer Group中的Consumer實(shí)例平均分?jǐn)傁M(fèi)消息。例如某個(gè)Topic有 9 條消息,其中一個(gè)Consumer Group有3個(gè)實(shí)例,那么每個(gè)實(shí)例只消費(fèi)其中的 3 條消息。即每一個(gè)隊(duì)列都把消息輪流分發(fā)給每個(gè)consumer。
14.5 ActiveMQ
點(diǎn)對(duì)點(diǎn)(p2p)、廣播(發(fā)布-訂閱)。
點(diǎn)對(duì)點(diǎn)模式,每個(gè)消息只有1個(gè)消費(fèi)者;發(fā)布/訂閱模式,每個(gè)消息可以有多個(gè)消費(fèi)者。
1)發(fā)送:
點(diǎn)對(duì)點(diǎn)模式:先要指定一個(gè)隊(duì)列,這個(gè)隊(duì)列會(huì)被創(chuàng)建或者已經(jīng)被創(chuàng)建。
發(fā)布/訂閱模式:先要指定一個(gè)topic,這個(gè)topic會(huì)被創(chuàng)建或者已經(jīng)被創(chuàng)建。
2)接收:
點(diǎn)對(duì)點(diǎn)模式:對(duì)于已經(jīng)創(chuàng)建了的隊(duì)列,消費(fèi)端要指定從哪一個(gè)隊(duì)列接收消息。
發(fā)布/訂閱模式:對(duì)于已經(jīng)創(chuàng)建了的topic,消費(fèi)端要指定訂閱哪一個(gè)topic的消息。
15、維度13:順序消息
Kafka:支持。
設(shè)置生產(chǎn)者的max.in.flight.requests.per.connection為1,可以保證消息是按照發(fā)送順序?qū)懭敕?wù)器的,即使發(fā)生了重試。
Kafka保證同一個(gè)分區(qū)里的消息是有序的,但是這種有序分兩種情況:
①key為null,消息逐個(gè)被寫入不同主機(jī)的分區(qū)中,但是對(duì)于每個(gè)分區(qū)依然是有序的
②key不為null , 消息被寫入到同一個(gè)分區(qū),這個(gè)分區(qū)的消息都是有序。
RabbitMQ:不支持
ZeroMQ:不支持
RocketMQ:支持
ActiveMQ:不支持
16、維度14:消息確認(rèn)
16.1 Kafka
支持。
1)發(fā)送方確認(rèn)機(jī)制:
ack=0,不管消息是否成功寫入分區(qū)
ack=1,消息成功寫入首領(lǐng)分區(qū)后,返回成功
ack=all,消息成功寫入所有分區(qū)后,返回成功。
2)接收方確認(rèn)機(jī)制:
自動(dòng)或者手動(dòng)提交分區(qū)偏移量,早期版本的Kafka偏移量是提交給Zookeeper的,這樣使得zookeeper的壓力比較大,更新版本的Kafka的偏移量是提交給Kafka服務(wù)器的,不再依賴于zookeeper群組,集群的性能更加穩(wěn)定。
16.2 RabbitMQ
支持。
1)發(fā)送方確認(rèn)機(jī)制,消息被投遞到所有匹配的隊(duì)列后,返回成功。如果消息和隊(duì)列是可持久化的,那么在寫入磁盤后,返回成功。支持批量確認(rèn)和異步確認(rèn)。
2)接收方確認(rèn)機(jī)制,設(shè)置autoAck為false,需要顯式確認(rèn),設(shè)置autoAck為true,自動(dòng)確認(rèn)。
當(dāng)autoAck為false的時(shí)候,RabbitMQ隊(duì)列會(huì)分成兩部分,一部分是等待投遞給consumer的消息,一部分是已經(jīng)投遞但是沒收到確認(rèn)的消息。
如果一直沒有收到確認(rèn)信號(hào),并且consumer已經(jīng)斷開連接,RabbitMQ會(huì)安排這個(gè)消息重新進(jìn)入隊(duì)列,投遞給原來的消費(fèi)者或者下一個(gè)消費(fèi)者。
未確認(rèn)的消息不會(huì)有過期時(shí)間,如果一直沒有確認(rèn),并且沒有斷開連接,RabbitMQ會(huì)一直等待,RabbitMQ允許一條消息處理的時(shí)間可以很久很久。
16.3 ZeroMQ
支持。
16.4 RocketMQ
支持。
16.5 ActiveMQ
支持。
17、維度15:消息回溯
1)Kafka:支持指定分區(qū)offset位置的回溯
2)RabbitMQ:不支持
3)ZeroMQ:不支持
4)RocketMQ:支持指定時(shí)間點(diǎn)的回溯
5)ActiveMQ:不支持
18、維度16:消息重試
18.1 Kafka
不支持,但是可以實(shí)現(xiàn)。
Kafka支持指定分區(qū)offset位置的回溯,可以實(shí)現(xiàn)消息重試。
18.2 RabbitMQ
不支持,但是可以利用消息確認(rèn)機(jī)制實(shí)現(xiàn)。
RabbitMQ接收方確認(rèn)機(jī)制,設(shè)置autoAck為false。
當(dāng)autoAck為false的時(shí)候,RabbitMQ隊(duì)列會(huì)分成兩部分,一部分是等待投遞給consumer的消息,一部分是已經(jīng)投遞但是沒收到確認(rèn)的消息。
如果一直沒有收到確認(rèn)信號(hào),并且consumer已經(jīng)斷開連接,RabbitMQ會(huì)安排這個(gè)消息重新進(jìn)入隊(duì)列,投遞給原來的消費(fèi)者或者下一個(gè)消費(fèi)者。
18.3 ZeroMQ
不支持。
18.4 RocketMQ
支持。
消息消費(fèi)失敗的大部分場景下,立即重試99%都會(huì)失敗,所以RocketMQ的策略是在消費(fèi)失敗時(shí)定時(shí)重試,每次時(shí)間間隔相同。
1)發(fā)送端的 send 方法本身支持內(nèi)部重試,重試邏輯如下:
至多重試3次;
如果發(fā)送失敗,則輪轉(zhuǎn)到下一個(gè)broker;
這個(gè)方法的總耗時(shí)不超過sendMsgTimeout 設(shè)置的值,默認(rèn) 10s,超過時(shí)間不在重試。
2)接收端:
Consumer 消費(fèi)消息失敗后,要提供一種重試機(jī)制,令消息再消費(fèi)一次。Consumer 消費(fèi)消息失敗通常可以分為以下兩種情況:
由于消息本身的原因,例如反序列化失敗,消息數(shù)據(jù)本身無法處理(例如話費(fèi)充值,當(dāng)前消息的手機(jī)號(hào)被注銷,無法充值)等。定時(shí)重試機(jī)制,比如過 10s 秒后再重試。
由于依賴的下游應(yīng)用服務(wù)不可用,例如 db 連接不可用,外系統(tǒng)網(wǎng)絡(luò)不可達(dá)等。即使跳過當(dāng)前失敗的消息,消費(fèi)其他消息同樣也會(huì)報(bào)錯(cuò)。這種情況可以 sleep 30s,再消費(fèi)下一條消息,減輕 Broker 重試消息的壓力。
18.5 ActiveMQ
不支持。
19、維度17:并發(fā)度
19.1 Kafka
并發(fā)度高。
一個(gè)線程一個(gè)消費(fèi)者,Kafka限制消費(fèi)者的個(gè)數(shù)要小于等于分區(qū)數(shù),如果要提高并行度,可以在消費(fèi)者中再開啟多線程,或者增加consumer實(shí)例數(shù)量。
19.2 RabbitMQ
并發(fā)度極高。本身是用Erlang語言寫的,并發(fā)性能高。
可在消費(fèi)者中開啟多線程,最常用的做法是一個(gè)channel對(duì)應(yīng)一個(gè)消費(fèi)者,每一個(gè)線程把持一個(gè)channel,多個(gè)線程復(fù)用connection的tcp連接,減少性能開銷。
當(dāng)RabbitMQ隊(duì)列擁有多個(gè)消費(fèi)者的時(shí)候,隊(duì)列收到的消息將以輪詢的分發(fā)方式發(fā)送給消費(fèi)者。每條消息只會(huì)發(fā)送給訂閱列表里的一個(gè)消費(fèi)者,不會(huì)重復(fù)。
這種方式非常適合擴(kuò)展,而且是專門為并發(fā)程序設(shè)計(jì)的。
如果某些消費(fèi)者的任務(wù)比較繁重,那么可以設(shè)置basicQos限制信道上消費(fèi)者能保持的最大未確認(rèn)消息的數(shù)量,在達(dá)到上限時(shí),RabbitMQ不再向這個(gè)消費(fèi)者發(fā)送任何消息。
19.3 ZeroMQ
并發(fā)度高。
19.4 RocketMQ
并發(fā)度高。
1)RocketMQ限制消費(fèi)者的個(gè)數(shù)少于等于隊(duì)列數(shù),但是可以在消費(fèi)者中再開啟多線程,這一點(diǎn)和Kafka是一致的,提高并行度的方法相同。
修改消費(fèi)并行度方法:
同一個(gè) ConsumerGroup 下,通過增加 Consumer 實(shí)例數(shù)量來提高并行度,超過訂閱隊(duì)列數(shù)的 Consumer實(shí)例無效。
提高單個(gè) Consumer 的消費(fèi)并行線程,通過修改參數(shù)consumeThreadMin、consumeThreadMax
2)同一個(gè)網(wǎng)絡(luò)連接connection,客戶端多個(gè)線程可以同時(shí)發(fā)送請(qǐng)求,連接會(huì)被復(fù)用,減少性能開銷。
19.5 ActiveMQ
并發(fā)度高。
單個(gè)ActiveMQ的接收和消費(fèi)消息的速度在1萬筆/秒(持久化 一般為1-2萬, 非持久化 2 萬以上),在生產(chǎn)環(huán)境中部署10個(gè)ActiveMQ就能達(dá)到10萬筆/秒以上的性能,部署越多的ActiveMQ broker 在MQ上latency也就越低,系統(tǒng)吞吐量也就越高。
附錄:更多架構(gòu)設(shè)計(jì)方面的文章
[1] 有關(guān)IM架構(gòu)設(shè)計(jì)的文章:
《淺談IM系統(tǒng)的架構(gòu)設(shè)計(jì)》
《簡述移動(dòng)端IM開發(fā)的那些坑:架構(gòu)設(shè)計(jì)、通信協(xié)議和客戶端》
《一套海量在線用戶的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)》
《一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案》
《從零到卓越:京東客服即時(shí)通訊系統(tǒng)的技術(shù)架構(gòu)演進(jìn)歷程》
《蘑菇街即時(shí)通訊/IM服務(wù)器開發(fā)之架構(gòu)選擇》
《騰訊QQ1.4億在線用戶的技術(shù)挑戰(zhàn)和架構(gòu)演進(jìn)之路PPT》
《微信后臺(tái)基于時(shí)間序的海量數(shù)據(jù)冷熱分級(jí)架構(gòu)設(shè)計(jì)實(shí)踐》
《微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡(演講全文)》
《如何解讀《微信技術(shù)總監(jiān)談架構(gòu):微信之道——大道至簡》》
《快速裂變:見證微信強(qiáng)大后臺(tái)架構(gòu)從0到1的演進(jìn)歷程(一)》
《17年的實(shí)踐:騰訊海量產(chǎn)品的技術(shù)方法論》
《移動(dòng)端IM中大規(guī)模群消息的推送如何保證效率、實(shí)時(shí)性?》
《現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲(chǔ)方案探討》
《IM開發(fā)基礎(chǔ)知識(shí)補(bǔ)課(二):如何設(shè)計(jì)大量圖片文件的服務(wù)端存儲(chǔ)架構(gòu)?》
《IM開發(fā)基礎(chǔ)知識(shí)補(bǔ)課(三):快速理解服務(wù)端數(shù)據(jù)庫讀寫分離原理及實(shí)踐建議》
《IM開發(fā)基礎(chǔ)知識(shí)補(bǔ)課(四):正確理解HTTP短連接中的Cookie、Session和Token》
《WhatsApp技術(shù)實(shí)踐分享:32人工程團(tuán)隊(duì)創(chuàng)造的技術(shù)神話》
《微信朋友圈千億訪問量背后的技術(shù)挑戰(zhàn)和實(shí)踐總結(jié)》
《王者榮耀2億用戶量的背后:產(chǎn)品定位、技術(shù)架構(gòu)、網(wǎng)絡(luò)方案等》
《IM系統(tǒng)的MQ消息中間件選型:Kafka還是RabbitMQ?》
《騰訊資深架構(gòu)師干貨總結(jié):一文讀懂大型分布式系統(tǒng)設(shè)計(jì)的方方面面》
《以微博類應(yīng)用場景為例,總結(jié)海量社交系統(tǒng)的架構(gòu)設(shè)計(jì)步驟》
《快速理解高性能HTTP服務(wù)端的負(fù)載均衡技術(shù)原理》
《子彈短信光鮮的背后:網(wǎng)易云信首席架構(gòu)師分享億級(jí)IM平臺(tái)的技術(shù)實(shí)踐》
《知乎技術(shù)分享:從單機(jī)到2000萬QPS并發(fā)的Redis高性能緩存實(shí)踐之路》
《IM開發(fā)基礎(chǔ)知識(shí)補(bǔ)課(五):通俗易懂,正確理解并用好MQ消息隊(duì)列》
《微信技術(shù)分享:微信的海量IM聊天消息序列號(hào)生成實(shí)踐(算法原理篇)》
《微信技術(shù)分享:微信的海量IM聊天消息序列號(hào)生成實(shí)踐(容災(zāi)方案篇)》
《新手入門:零基礎(chǔ)理解大型分布式架構(gòu)的演進(jìn)歷史、技術(shù)原理、最佳實(shí)踐》
《一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐》
《阿里技術(shù)分享:深度揭秘阿里數(shù)據(jù)庫技術(shù)方案的10年變遷史》
《阿里技術(shù)分享:阿里自研金融級(jí)數(shù)據(jù)庫OceanBase的艱辛成長之路》
《社交軟件紅包技術(shù)解密(一):全面解密QQ紅包技術(shù)方案——架構(gòu)、技術(shù)實(shí)現(xiàn)等》
《社交軟件紅包技術(shù)解密(二):解密微信搖一搖紅包從0到1的技術(shù)演進(jìn)》
《社交軟件紅包技術(shù)解密(三):微信搖一搖紅包雨背后的技術(shù)細(xì)節(jié)》
《社交軟件紅包技術(shù)解密(四):微信紅包系統(tǒng)是如何應(yīng)對(duì)高并發(fā)的》
《社交軟件紅包技術(shù)解密(五):微信紅包系統(tǒng)是如何實(shí)現(xiàn)高可用性的》
《社交軟件紅包技術(shù)解密(六):微信紅包系統(tǒng)的存儲(chǔ)層架構(gòu)演進(jìn)實(shí)踐》
《社交軟件紅包技術(shù)解密(七):支付寶紅包的海量高并發(fā)技術(shù)實(shí)踐》
《社交軟件紅包技術(shù)解密(八):全面解密微博紅包技術(shù)方案》
《社交軟件紅包技術(shù)解密(九):談?wù)勈諵紅包的功能邏輯、容災(zāi)、運(yùn)維、架構(gòu)等》
《即時(shí)通訊新手入門:一文讀懂什么是Nginx?它能否實(shí)現(xiàn)IM的負(fù)載均衡?》
《即時(shí)通訊新手入門:快速理解RPC技術(shù)——基本概念、原理和用途》
《多維度對(duì)比5款主流分布式MQ消息隊(duì)列,媽媽再也不擔(dān)心我的技術(shù)選型了》
>> 更多同類文章 ……
[2] 更多其它架構(gòu)設(shè)計(jì)相關(guān)文章:
《騰訊資深架構(gòu)師干貨總結(jié):一文讀懂大型分布式系統(tǒng)設(shè)計(jì)的方方面面》
《快速理解高性能HTTP服務(wù)端的負(fù)載均衡技術(shù)原理》
《子彈短信光鮮的背后:網(wǎng)易云信首席架構(gòu)師分享億級(jí)IM平臺(tái)的技術(shù)實(shí)踐》
《知乎技術(shù)分享:從單機(jī)到2000萬QPS并發(fā)的Redis高性能緩存實(shí)踐之路》
《新手入門:零基礎(chǔ)理解大型分布式架構(gòu)的演進(jìn)歷史、技術(shù)原理、最佳實(shí)踐》
《阿里技術(shù)分享:深度揭秘阿里數(shù)據(jù)庫技術(shù)方案的10年變遷史》
《阿里技術(shù)分享:阿里自研金融級(jí)數(shù)據(jù)庫OceanBase的艱辛成長之路》
《達(dá)達(dá)O2O后臺(tái)架構(gòu)演進(jìn)實(shí)踐:從0到4000高并發(fā)請(qǐng)求背后的努力》
《優(yōu)秀后端架構(gòu)師必會(huì)知識(shí):史上最全MySQL大表優(yōu)化方案總結(jié)》
《小米技術(shù)分享:解密小米搶購系統(tǒng)千萬高并發(fā)架構(gòu)的演進(jìn)和實(shí)踐》
《一篇讀懂分布式架構(gòu)下的負(fù)載均衡技術(shù):分類、原理、算法、常見方案等》
《通俗易懂:如何設(shè)計(jì)能支撐百萬并發(fā)的數(shù)據(jù)庫架構(gòu)?》
《多維度對(duì)比5款主流分布式MQ消息隊(duì)列,媽媽再也不擔(dān)心我的技術(shù)選型了》
>> 更多同類文章 ……
(本文同步發(fā)布于:http://www.52im.net/thread-2625-1-1.html)