RabbitMQ和Kafka比較選擇

前言

開源社區有好多優秀的隊列中間件,比如RabbitMQ和Kafka,每個隊列都貌似有其特性,在進行工程選擇時,往往眼花繚亂,不知所措。對於RabbitMQ和Kafka,到底應該選哪個?

RabbitMQ架構

RabbitMQ是一個分佈式系統,這裏面有幾個抽象概念。

  • broker:每個節點運行的服務程序,功能爲維護該節點的隊列的增刪以及轉發隊列操作請求。
  • master queue:每個隊列都分爲一個主隊列和若干個鏡像隊列。
  • mirror queue:鏡像隊列,作爲master queue的備份。在master queue所在節點掛掉之後,系統把mirror queue提升爲master queue,負責處理客戶端隊列操作請求。注意,mirror queue只做鏡像,設計目的不是爲了承擔客戶端讀寫壓力。

如上圖所示,集羣中有兩個節點,每個節點上有一個broker,每個broker負責本機上隊列的維護,並且borker之間可以互相通信。集羣中有兩個隊列A和B,每個隊列都分爲master queue和mirror queue(備份)。那麼隊列上的生產消費怎麼實現的呢?

隊列消費

如上圖有兩個consumer消費隊列A,這兩個consumer連在了集羣的不同機器上。RabbitMQ集羣中的任何一個節點都擁有集羣上所有隊列的元信息,所以連接到集羣中的任何一個節點都可以,主要區別在於有的consumer連在master queue所在節點,有的連在非master queue節點上。

因爲mirror queue要和master queue保持一致,故需要同步機制,正因爲一致性的限制,導致所有的讀寫操作都必須都操作在master queue上(想想,爲啥讀也要從master queue中讀?和數據庫讀寫分離是不一樣的。),然後由master節點同步操作到mirror queue所在的節點。即使consumer連接到了非master queue節點,該consumer的操作也會被路由到master queue所在的節點上,這樣才能進行消費。

隊列生產

原理和消費一樣,如果連接到非 master queue 節點,則路由過去。

所以,到這裏小夥伴們就可以看到 RabbitMQ的不足:由於master queue單節點,導致性能瓶頸,吞吐量受限。雖然爲了提高性能,內部使用了Erlang這個語言實現,但是終究擺脫不了架構設計上的致命缺陷。

Kafka

說實話,Kafka我覺得就是看到了RabbitMQ這個缺陷才設計出的一個改進版,改進的點就是:把一個隊列的單一master變成多個master,即一臺機器扛不住qps,那麼我就用多臺機器扛qps,把一個隊列的流量均勻分散在多臺機器上不就可以了麼?注意,多個master之間的數據沒有交集,即一條消息要麼發送到這個master queue,要麼發送到另外一個master queue。

這裏面的每個master queue 在Kafka中叫做Partition,即一個分片。一個隊列有多個主分片,每個主分片又有若干副分片做備份,同步機制類似於RabbitMQ。

如上圖,我們省略了不同的queue,假設集羣上只有一個queue(Kafka中叫Topic)。每個生產者隨機把消息發送到主分片上,之後主分片再同步給副分片。

隊列讀取的時候虛擬出一個Group的概念,一個Topic內部的消息,只會路由到同Group內的一個consumer上,同一個Group中的consumer消費的消息是不一樣的;Group之間共享一個Topic,看起來就是一個隊列的多個拷貝。所以,爲了達到多個Group共享一個Topic數據,Kafka並不會像RabbitMQ那樣消息消費完畢立馬刪除,而是必須在後臺配置保存日期,即只保存最近一段時間的消息,超過這個時間的消息就會從磁盤刪除,這樣就保證了在一個時間段內,Topic數據對所有Group可見(這個特性使得Kafka非常適合做一個公司的數據總線)。隊列讀同樣是讀主分片,並且爲了優化性能,消費者與主分片有一一的對應關係,如果消費者數目大於分片數,則存在某些消費者得不到消息。

由此可見,Kafka絕對是爲了高吞吐量設計的,比如設置分片數爲100,那麼就有100臺機器去扛一個Topic的流量,當然比RabbitMQ的單機性能好。

總結

本文只做了Kafka和RabbitMQ的對比,但是開源隊列豈止這兩個,ZeroMQ,RocketMQ,JMQ等等,時間有限也就沒有細看,故不在本文比較範圍之內。

所以,別再被這些五花八門的隊列迷惑了,從架構上找出關鍵差別,並結合自己的實際需求(比如本文就只單單從吞吐量一個需求來考察)輕輕鬆鬆搞定選型。最後總結如下:

  • 吞吐量較低:Kafka和RabbitMQ都可以。
  • 吞吐量高:Kafka。
  •  

我們在吞吐量方面比較了Kafka和RabbitMQ,知道了Kafka的吞吐量要高於RabbitMQ。本文從可靠性方面繼續探討兩個隊列的差異。

RabbitMQ可靠性

我們通過前文知道,RabbitMQ的隊列分爲master queue和mirror queue,mirror queue 在master queue宕機之後,會被提升爲master queue,如下圖所示。  隊列A的consumer在消費的時候,機器宕機,此時客戶端和服務端分別做如下動作:

  • 服務端:把mirror queue提升爲master queue
  • 客戶端:連接到新的master queue 所在的節點進行消費或者生產

當master queue 所在節點宕機後,其正在被消費的消息的相關信息全部丟失,即服務端不知道消費者對那一瞬間消費的消息是否進行了ACK,所以在mirror queue被提升爲master queue時,會把宕機前正在進行消費的的消息全部重新發送一遍,即客戶端重連後,消息可能被重複消費,這個時候就必須依靠應用層邏輯來判斷來避免重複消費。

在持久化方面,RabbitMQ的master queue每次收到新消息後,都會立刻寫入磁盤,並把消息同步給mirror queue。假設在master queue 收到消息後,消息未同步到mirror queue 之前master queue 宕機,則此時mirror queue中就沒有剛剛master queue收到的那條消息,當這個mirror queue被提升爲master queue時,消費者連接到新的master queue上進行消費時就丟了一條消息。所以,RabbitMQ也會丟消息,只不過這個丟消息的概率非常低。

Kafka可靠性

我們知道Kafka中的每個隊列叫做Topic,一個Topic有多個主分片和副分片,當主分片所在機器宕機後,服務端會把一個副分片提升爲主分片,如下圖所示。 

服務端和客戶端會有如下動作:

  • 服務端:把副分片提升爲主分片
  • 客戶端:連接到新的主分片

Kafka同樣有主從同步,所以也必定存在與RabbitMQ同樣丟消息的問題。但是Kafka的每個客戶端保存了讀取消息的偏移信息,故當一個主分片宕機後,Kafka客戶端可以從副分片相應位移後繼續消費,不會有重複消費的情況。

持久化方面,Kafka默認把消息直接寫文件,但是由於操作系統的cache原因,消息可能不會立馬寫到磁盤上,這個時候就需要刷新文件到磁盤。由於刷新文件到磁盤是一個比較耗時的操作,故Kafka提供了兩種不同的刷新配置:

#每接收多少條消息刷一下磁盤
log.flush.interval.messages=10000
#每隔多少ms刷一下磁盤
log.flush.interval.ms=1000

我們完全可以把log.flush.interval.messages設置爲1,這樣Kafka就能在持久化方面達到和RabbitMQ同樣的安全級別。

但是Kafka集羣依賴ZK,如上圖所示,所以對於Kafka穩定性的評估必須考慮ZK集羣的穩定性,而一般我們認爲任何分佈式集羣的穩定性都小於1,故兩個集羣的串聯穩定性會下降一些,維護更復雜一些,這點沒有RabbitMQ有優勢。

總結

其實好多開源組件隨着時間推移,往往都進行了各種改進。就比如Kafka雖然是爲了日誌而生,給人第一印象是容易丟消息,但是經過這麼多年的改進,其可靠性可能並不遜色RabbitMQ了,只需要你根據不同的業務場景配置不同的配置參數,即可達到適合自己的安全級別。

  • 從吞吐量上看,在不要求消息順序情況下,Kafka完勝;在要求消息先後順序的場景,性能應該稍遜RabbitMQ(此時Kafka的分片數只能爲1)。
  • 從穩定性來看,RabbitMQ勝出,但是Kafka也並不遜色多少。

好了,以上就是我的個人分析,多有不足,希望能和小夥伴進行探討。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章