Kafka和RabbitMQ有哪些區(qū)別川蒙,各自適合什么場景蚜厉?

有個(gè) xx 需求,我應(yīng)該用 Kafka 還是 RabbitMQ 畜眨?

下面我會(huì)通過 6 個(gè)場景昼牛,來對比分析一下 Kafka 和 RabbitMQ 的優(yōu)劣。

一康聂、消息的順序

有這樣一個(gè)需求:當(dāng)訂單狀態(tài)變化的時(shí)候贰健,把訂單狀態(tài)變化的消息發(fā)送給所有關(guān)心訂單變化的系統(tǒng)。
訂單會(huì)有創(chuàng)建成功恬汁、待付款伶椿、已支付、已發(fā)貨的狀態(tài),狀態(tài)之間是單向流動(dòng)的脊另。


圖片.png

好导狡,現(xiàn)在我們把訂單狀態(tài)變化消息要發(fā)送給所有關(guān)心訂單狀態(tài)的系統(tǒng)上去,實(shí)現(xiàn)方式就是用消息隊(duì)列偎痛。


圖片.png

在這種業(yè)務(wù)下旱捧,我們最想要的是什么?
*消息的順序:對于同一筆訂單來說踩麦,狀態(tài)的變化都是有嚴(yán)格的先后順序的枚赡。
*吞吐量:像訂單的業(yè)務(wù),我們自然希望訂單越多越好谓谦。訂單越多贫橙,吞吐量就越大。

**在這種情況下反粥,我們先看看 RabbitMQ 是怎么做的卢肃。
首先,對于發(fā)消息星压,并廣播給多個(gè)消費(fèi)者這種情況践剂,RabbitMQ 會(huì)為每個(gè)消費(fèi)者建立一個(gè)對應(yīng)的隊(duì)列。也就是說娜膘,如果有 10 個(gè)消費(fèi)者逊脯,RabbitMQ 會(huì)建立 10 個(gè)對應(yīng)的隊(duì)列。然后竣贪,當(dāng)一條消息被發(fā)出后军洼,RabbitMQ 會(huì)把這條消息復(fù)制 10 份放到這 10 個(gè)隊(duì)列里。


圖片.png

當(dāng) RabbitMQ 把消息放入到對應(yīng)的隊(duì)列后演怎,我們緊接著面臨的問題就是匕争,我們應(yīng)該在系統(tǒng)內(nèi)部啟動(dòng)多少線程去從消息隊(duì)列中獲取消息。

如果只是單線程去獲取消息爷耀,那自然沒有什么好說的甘桑。但是多線程情況,可能就會(huì)有問題了……

RabbitMQ 有這么個(gè)特性歹叮,它在官方文檔就聲明了自己是不保證多線程消費(fèi)同一個(gè)隊(duì)列的消息跑杭,一定保證順序的。而不保證的原因咆耿,是因?yàn)槎嗑€程時(shí)德谅,當(dāng)一個(gè)線程消費(fèi)消息報(bào)錯(cuò)的時(shí)候,RabbitMQ 會(huì)把消費(fèi)失敗的消息再入隊(duì)萨螺,此時(shí)就可能出現(xiàn)亂序的情況窄做。


圖片.png

T0 時(shí)刻愧驱,隊(duì)列中有四條消息 A1、B1椭盏、B2组砚、A2。其中 A1庸汗、A2 表示訂單 A 的兩個(gè)狀態(tài):待付款惫确、已付款。B1蚯舱、B2 也同理,是訂單 B 的待付款掩蛤、已付款枉昏。

到了 T1 時(shí)刻,消息 A1 被線程 1 收到揍鸟,消息 B1 被線程 2 收到兄裂。此時(shí),一切都還正常阳藻。

到了 T3 時(shí)刻晰奖,B1 消費(fèi)出錯(cuò)了,同時(shí)呢腥泥,由于線程 1 處理速度快匾南,又從消息隊(duì)列中獲取到了 B2。此時(shí)蛔外,問題開始出現(xiàn)蛆楞。

到了 T4 時(shí)刻,由于 RabbitMQ 線程消費(fèi)出錯(cuò)夹厌,可以把消息重新入隊(duì)的特性豹爹,此時(shí) B1 會(huì)被重新放到隊(duì)列頭部。所以矛纹,如果不湊巧臂聋,線程 1 獲取到了 B1,就出現(xiàn)了亂序情況或南,B2 狀態(tài)明明是 B1 的后續(xù)狀態(tài)孩等,卻被提前處理了。

所以迎献,可以看到了瞎访,這個(gè)場景用 RabbitMQ,出現(xiàn)了三個(gè)問題:
*為了實(shí)現(xiàn)發(fā)布訂閱功能吁恍,從而使用的消息復(fù)制扒秸,會(huì)降低性能并耗費(fèi)更多資源
*多個(gè)消費(fèi)者無法嚴(yán)格保證消息順序
*大量的訂單集中在一個(gè)隊(duì)列播演,吞吐量受到了限制
那么 Kafka 怎么樣呢?Kafka 正好在這三個(gè)問題上伴奥,表現(xiàn)的要比 RabbitMQ 要好得多写烤。

首先,Kafka 的發(fā)布訂閱并不會(huì)復(fù)制消息拾徙,因?yàn)?Kafka 的發(fā)布訂閱就是消費(fèi)者直接去獲取被 Kafka 保存在日志文件中的消息就好洲炊。無論是多少消費(fèi)者,他們只需要主動(dòng)去找到消息在文件中的位置即可尼啡。
其次暂衡,Kafka 不會(huì)出現(xiàn)消費(fèi)者出錯(cuò)后,把消息重新入隊(duì)的現(xiàn)象崖瞭。
最后狂巢,Kafka 可以對訂單進(jìn)行分區(qū),把不同訂單分到多個(gè)分區(qū)中保存书聚,這樣唧领,吞吐量能更好。
所以雌续,對于這個(gè)需求 Kafka 更合適斩个。

二、消息的匹配

我曾經(jīng)做過一套營銷系統(tǒng)驯杜。這套系統(tǒng)中有個(gè)非常顯著的特點(diǎn)受啥,就是非常復(fù)雜非常靈活地匹配規(guī)則。
比如艇肴,要根據(jù)推廣內(nèi)容去匹配不同的方式做宣傳腔呜。又比如,要根據(jù)不同的活動(dòng)去匹配不同的渠道去做分發(fā)再悼。
總之核畴,數(shù)不清的匹配規(guī)則是這套系統(tǒng)中非常重要的一個(gè)特點(diǎn)。


圖片.png

首先冲九,先看看 RabbitMQ 的谤草,你會(huì)發(fā)現(xiàn) RabbitMQ 是允許在消息中添加 routing_key 或者自定義消息頭,然后通過一些特殊的 Exchange莺奸,很簡單的就實(shí)現(xiàn)了消息匹配分發(fā)丑孩。開發(fā)幾乎不用成本。
而 Kafka 呢灭贷?如果你要實(shí)現(xiàn)消息匹配温学,開發(fā)成本高多了胸竞。

首先柿祈,通過簡單的配置去自動(dòng)匹配和分發(fā)到合適的消費(fèi)者端這件事是不可能的颤芬。

其次擎析,消費(fèi)者端必須先把所有消息不管需要不需要,都取出來轧拄。然后揽祥,再根據(jù)業(yè)務(wù)需求,自己去實(shí)現(xiàn)各種精準(zhǔn)和模糊匹配檩电≈舴幔可能因?yàn)檫^度的復(fù)雜性,還要引入規(guī)則引擎俐末。

這個(gè)場景下 RabbitMQ 扳回一分料按。

三、消息的超時(shí)

在電商業(yè)務(wù)里鹅搪,有個(gè)需求:下單之后站绪,如果用戶在 15 分鐘內(nèi)未支付,則自動(dòng)取消訂單丽柿。
你可能奇怪,這種怎么也會(huì)用到消息隊(duì)列的魂挂?
我來先簡單解釋一下甫题,在單一服務(wù)的系統(tǒng),可以起個(gè)定時(shí)任務(wù)就搞定了涂召。

但是坠非,在 SOA 或者微服務(wù)架構(gòu)下,這樣做就不行了果正。因?yàn)楹芏鄠€(gè)服務(wù)都關(guān)心是否支付這件事炎码,如果每種服務(wù),都自己實(shí)現(xiàn)一套定時(shí)任務(wù)的邏輯秋泳,既重復(fù)潦闲,又難以維護(hù)。

在這種情況下迫皱,我們往往會(huì)做一層抽象:把要執(zhí)行的任務(wù)封裝成消息歉闰。當(dāng)時(shí)間到了,直接扔到消息隊(duì)列里卓起,消息的訂閱者們獲取到消息后和敬,直接執(zhí)行即可。

希望把消息延遲一定時(shí)間再處理的戏阅,被稱為延遲隊(duì)列昼弟。

對于訂單取消的這種業(yè)務(wù),我們就會(huì)在創(chuàng)建訂單的時(shí)候奕筐,同時(shí)扔一個(gè)包含了執(zhí)行任務(wù)信息的消息到延遲隊(duì)列舱痘,指定15分鐘后变骡,讓訂閱這個(gè)隊(duì)列的各個(gè)消費(fèi)者,可以收到這個(gè)消息衰粹。隨后锣光,各個(gè)消費(fèi)者所在的系統(tǒng)就可以去執(zhí)行相關(guān)的掃描訂單的任務(wù)了。


圖片.png

RabbitMQ 和 Kafka 消息隊(duì)列如何選铝耻?

先看下 RabbitMQ 的誊爹。

RabbitMQ 的消息自帶手表,消息中有個(gè) TTL 字段瓢捉,可以設(shè)置消息在 RabbitMQ 中的存放的時(shí)間频丘,超時(shí)了會(huì)被移送到一個(gè)叫死信隊(duì)列的地方。

所以泡态,延遲隊(duì)列 RabbitMQ 最簡單的實(shí)現(xiàn)方式就是設(shè)置 TTL搂漠,然后一個(gè)消費(fèi)者去監(jiān)聽死信隊(duì)列。當(dāng)消息超時(shí)了某弦,監(jiān)聽死信隊(duì)列的消費(fèi)者就收到消息了桐汤。

不過,這樣做有個(gè)大問題:假設(shè)靶壮,我們先往隊(duì)列放入一條過期時(shí)間是 10 秒的 A 消息怔毛,再放入一條過期時(shí)間是 5 秒的 B 消息。 那么問題來了腾降,B 消息會(huì)先于 A 消息進(jìn)入死信隊(duì)列嗎拣度?

答案是否定的。B 消息會(huì)優(yōu)先遵守隊(duì)列的先進(jìn)先出規(guī)則螃壤,在 A 消息過期后抗果,和其一起進(jìn)入死信隊(duì)列被消費(fèi)者消費(fèi)。
在 RabbitMQ 的 3.5.8 版本以后奸晴,官方推薦的 rabbitmq delayed message exchange 插件可以解決這個(gè)問題冤馏。

*用了這個(gè)插件,我們在發(fā)送消息的時(shí)候蚁滋,把消息發(fā)往一個(gè)特殊的 Exchange宿接。
*同時(shí),在消息頭里指定要延遲的時(shí)間辕录。
*收到消息的 Exchange 并不會(huì)立即把消息放到隊(duì)列里睦霎,而是在消息延遲時(shí)間到達(dá)后,才會(huì)把消息放入走诞。
圖片.png

再看下 Kafka 的:


圖片.png

Kafka 要實(shí)現(xiàn)延遲隊(duì)列就很麻煩了副女。
*你先需要把消息先放入一個(gè)臨時(shí)的 topic。
*然后得自己開發(fā)一個(gè)做中轉(zhuǎn)的消費(fèi)者蚣旱。讓這個(gè)中間的消費(fèi)者先去把消息從這個(gè)臨時(shí)的 topic 取出來碑幅。
*取出來戴陡,這消息還不能馬上處理啊,因?yàn)闆]到時(shí)間呢沟涨。也沒法保存在自己的內(nèi)存里恤批,怕崩潰了,消息沒了裹赴。所以喜庞,就得把沒有到時(shí)間的消息存入到數(shù)據(jù)庫里。
存入數(shù)據(jù)庫中的消息需要在時(shí)間到了之后再放入到 Kafka 里棋返,以便真正的消費(fèi)者去執(zhí)行真正的業(yè)務(wù)邏輯延都。
……
想想就已經(jīng)頭大了,這都快搞成調(diào)度平臺(tái)了睛竣。再高級點(diǎn)晰房,還要用時(shí)間輪算法才能更好更準(zhǔn)確。
這次射沟,RabbitMQ 上那一條條戴手表的消息殊者,才是最好的選擇。

四验夯、消息的保持

在微服務(wù)里幽污,事件溯源模式是經(jīng)常用到的。如果想用消息隊(duì)列實(shí)現(xiàn)簿姨,一般是把事件當(dāng)成消息,依次發(fā)送到消息隊(duì)列中簸搞。

事件溯源有個(gè)最經(jīng)典的場景扁位,就是事件的重放。簡單來講就是把系統(tǒng)中某段時(shí)間發(fā)生的事件依次取出來再處理趁俊。而且域仇,根據(jù)業(yè)務(wù)場景不同,這些事件重放很可能不是一次寺擂,更可能是重復(fù) N 次暇务。

假設(shè),我們現(xiàn)在需要一批在線事件重放怔软,去排查一些問題垦细。
RabbitMQ 此時(shí)就真的不行了,因?yàn)橄⒈蝗巳〕鰜砭捅粍h除了挡逼。想再次被重復(fù)消費(fèi)括改?對不起。
而 Kafka 呢家坎,消息會(huì)被持久化一個(gè)專門的日志文件里嘱能。不會(huì)因?yàn)楸幌M(fèi)了就被刪除吝梅。
所以,對消息不離不棄的 Kafka 相對用過就拋的 RabbitMQ惹骂,請選擇 Kafka苏携。

五、消息的錯(cuò)誤處理

很多時(shí)候对粪,在做記錄數(shù)據(jù)相關(guān)業(yè)務(wù)的時(shí)候右冻,Kafka 一般是不二選擇。不過衩侥,有時(shí)候在記錄數(shù)據(jù)吞吐量不大時(shí)国旷,我自己倒是更喜歡用 RabbitMQ。

原因就是 Kafka 有一個(gè)我很不喜歡的設(shè)計(jì)原則:
當(dāng)單個(gè)分區(qū)中的消息一旦出現(xiàn)消費(fèi)失敗茫死,就只能停止而不是跳過這條失敗的消息繼續(xù)消費(fèi)后面的消息跪但。即不允許消息空洞。
只要消息出現(xiàn)失敗峦萎,不管是 Kafka 自身消息格式的損壞屡久,還是消費(fèi)者處理出現(xiàn)異常,是不允許跳過消費(fèi)失敗的消息繼續(xù)往后消費(fèi)的爱榔。
所以被环,在數(shù)據(jù)統(tǒng)計(jì)不要求十分精確的場景下選了 Kafka,一旦出現(xiàn)了消息消費(fèi)問題详幽,就會(huì)發(fā)生項(xiàng)目不可用的情況筛欢。這真是徒增煩惱。

而 RabbitMQ 呢唇聘,它由于會(huì)在消息出問題或者消費(fèi)錯(cuò)誤的時(shí)候版姑,可以重新入隊(duì)或者移動(dòng)消息到死信隊(duì)列,繼續(xù)消費(fèi)后面的迟郎,會(huì)省心很多剥险。

壞消息就像群眾中的壞蛋那樣,Kafka 處理這種壞蛋太過殘暴宪肖,非得把壞蛋揪出來不行表制。相對來說,RabbitMQ 就溫柔多了控乾,群眾是群眾么介,壞蛋是壞蛋,分開處理嘛阱持。

六夭拌、消息的吞吐量

Kafka 是每秒幾十萬條消息吞吐,而 RabbitMQ 的吞吐量是每秒幾萬條消息。

其實(shí)鸽扁,在一家公司內(nèi)部蒜绽,有必須用到 Kafka 那么大吞吐量的項(xiàng)目真的很少。大部分項(xiàng)目桶现,像 RabbitMQ 那樣每秒幾萬的消息吞吐躲雅,已經(jīng)非常夠了。

在一些沒那么大吞吐量的項(xiàng)目中引入 Kafka骡和,我覺得就不如引入 RabbitMQ相赁。
為什么呢?
因?yàn)?Kafka 為了更好的吞吐量慰于,很大程度上增加了自己的復(fù)雜度钮科。而這些復(fù)雜度對項(xiàng)目來說,就是麻煩婆赠,主要體現(xiàn)在兩個(gè)方面:

1绵脯、配置復(fù)雜、維護(hù)復(fù)雜

Kafka 的參數(shù)配置相對 RabbitMQ 是很復(fù)雜的休里。比如:磁盤管理相關(guān)參數(shù)蛆挫,集群管理相關(guān)參數(shù),ZooKeeper 交互相關(guān)參數(shù)妙黍,Topic 級別相關(guān)參數(shù)等悴侵,都需要一些思考和調(diào)優(yōu)。

另外拭嫁,Kafka 本身集群和參與管理集群的 ZooKeeper可免,這就帶來了更多的維護(hù)成本。Kafka 要用好做粤,你要考慮 JVM巴元,消息持久化,集群本身交互驮宴,以及 ZooKeeper 本身和它與 Kafka 之間的可靠和效率。

2呕缭、用好堵泽,用對存在門檻

Kafka 的 Producer 和 Consumer 本身要用好用對也存在很高的門檻。
比如恢总,Producer 消息可靠性保障迎罗、冪等性、事務(wù)消息等片仿,都需要對 KafkaProducer 有深入的了解纹安。
而 Consumer 更不用說了,光是一個(gè)日志偏移管理就讓一大堆人掉了不少頭發(fā)。

相對來說厢岂,RabbitMQ 就簡單得多光督。你可能都不用配置什么,直接啟動(dòng)起來就能很穩(wěn)定可靠地使用了塔粒。就算配置结借,也是寥寥幾個(gè)參數(shù)設(shè)置即可。

所以卒茬,大家在項(xiàng)目中引入消息隊(duì)列的時(shí)候船老,真的要好好考慮下,不要因?yàn)榇蠹叶脊拇?Kafka 好圃酵,就無腦引入柳畔。

總結(jié)

可以看到,如果我們要做消息隊(duì)列選型郭赐,有兩件事是必須要做好的:

*列出業(yè)務(wù)最重要的幾個(gè)特點(diǎn)

*深入到消息隊(duì)列的細(xì)節(jié)中去比較

等我們對這些中間件的特點(diǎn)非常熟悉之后薪韩,甚至可以把業(yè)務(wù)分解成不同的子業(yè)務(wù),再根據(jù)不同的子業(yè)務(wù)的特征堪置,引入不同的消息隊(duì)列躬存,即消息隊(duì)列混用。這樣舀锨,我們就可能會(huì)最大化我們的獲益岭洲,最小化我們的成本。
說了這么多坎匿,其實(shí)還有很多 Kafka 和 RabbitMQ 的比較沒有說盾剩,比如二者集群的區(qū)別,占用資源多少的比較等替蔬。以后有機(jī)會(huì)可以再提提告私。

總之,期待大家看完這篇文章后承桥,能對 Kafka 和 RabbitMQ 的區(qū)別有了更細(xì)節(jié)性的了解驻粟。
最后,分享一個(gè)網(wǎng)上的比較全的對比圖:


圖片.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凶异,一起剝皮案震驚了整個(gè)濱河市蜀撑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌剩彬,老刑警劉巖酷麦,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異喉恋,居然都是意外死亡沃饶,警方通過查閱死者的電腦和手機(jī)母廷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來糊肤,“玉大人琴昆,你說我怎么就攤上這事⌒郑” “怎么了椎咧?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長把介。 經(jīng)常有香客問我勤讽,道長,這世上最難降的妖魔是什么拗踢? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任脚牍,我火速辦了婚禮,結(jié)果婚禮上巢墅,老公的妹妹穿的比我還像新娘诸狭。我一直安慰自己,他們只是感情好君纫,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布驯遇。 她就那樣靜靜地躺著,像睡著了一般蓄髓。 火紅的嫁衣襯著肌膚如雪叉庐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天会喝,我揣著相機(jī)與錄音陡叠,去河邊找鬼。 笑死肢执,一個(gè)胖子當(dāng)著我的面吹牛枉阵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播预茄,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼兴溜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了耻陕?” 一聲冷哼從身側(cè)響起昵慌,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淮蜈,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體已卷,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梧田,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裁眯。...
    茶點(diǎn)故事閱讀 38,814評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鹉梨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出穿稳,到底是詐尸還是另有隱情存皂,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布逢艘,位于F島的核電站旦袋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏它改。R本人自食惡果不足惜疤孕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望央拖。 院中可真熱鬧祭阀,春花似錦、人聲如沸鲜戒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遏餐。三九已至伦腐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間境输,已是汗流浹背蔗牡。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗅剖,地道東北人辩越。 一個(gè)月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像信粮,于是被迫代替她去往敵國和親黔攒。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容