最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?

作者 | Alok Nikhil 、Vinoth Chandar

譯者 | 平川

策劃 | Tina

本文最初發佈於 Confluent 官方博客,經授權由 InfoQ 中文站翻譯並分享。

Apache Kafka 是最流行的事件流處理系統之一。在這個領域中有許多比較系統的方法,但是每個人都關心的一件事是性能。Kafka 的快眾所周知,但現如今它有多快,與其他系統相比又如何?我們決定在最新的雲硬體上測試下 Kafka 的性能。

為了進行比較,我們選擇了一個傳統的消息代理 RabbitMQ 和一個基於 Apache BookKeeper 的消息代理 Apache Pulsar。我們主要關注系統吞吐量和系統延遲,因為它們是生產中事件流處理系統的主要性能指標。具體來說,吞吐量測試測量每個系統在硬體(特別是磁碟和 CPU)使用方面的效率。延遲測試測量每個系統傳遞實時消息的差別,包括 99.9 百分位尾延遲,這是實時任務關鍵型應用程序以及微服務架構的核心要求。

我們發現,Kafka 提供了最好的吞吐量,同時提供了最低的端到端延遲(99.9 百分位)。在吞吐量比較低時,RabbitMQ 傳遞消息的延遲非常低。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?

當吞吐量高於 30MB/s 時,RabbitMQ 的延遲會顯著降低。此外,當吞吐量較高時,鏡像影響顯著,而更低的延遲則可以通過只使用經典隊列而不使用鏡像來實現。

本文首先介紹了我們使用的基準測試框架,然後介紹了測試平台和工作負載。最後將使用不同的系統和應用程序指標對結果進行解釋。所有這些都是開源的,所以感興趣的讀者可以自己重新生成結果,或者更深入地挖礦收集到的 Prometheus 指標。與大多數基準測試一樣,我們比較特定工作負載下的性能。我們總是鼓勵讀者使用自己的工作負載 / 設置進行比較,以理解這些工作負載 / 設置如何轉換為生產部署。

1 背景

首先,讓我們簡要地討論下每個系統,以了解它們的高級設計和架構,看下每個系統所做的權衡。

Kafka 是一個開源的分散式事件流處理平台,也是 Apache 軟體基金會下五個最活躍的項目之一。在其核心,Kafka 被設計成一個多副本的分散式持久化提交日誌,用於支撐事件驅動的微服務或大規模流處理應用程序。客戶端向代理集群提供事件或使用代理集群的事件,而代理會向底層文件系統寫入或從底層文件系統讀取事件,並自動在集群中同步或非同步地複製事件,以實現容錯性和高可用性。

Pulsar 是一個開源的分散式發布 / 訂閱消息系統,最初是服務於隊列用例的。最近,它又增加了事件流處理功能。Pulsar 被設計為一個(幾乎)無狀態代理實例層,它連接到單獨的 BookKeeper 實例層,由它實際地讀取 / 寫入消息,也可以選擇持久地存儲 / 複製消息。Pulsar 並不是唯一的同類系統,還有其他類似的消息傳遞系統,如 Apache DistributedLog 和 Pravega,它們都是在 BookKeeper 之上構建的,也是旨在提供一些類似 Kafka 的事件流處理功能。

BookKeeper 是一個開源的分散式存儲服務,最初是為 Apache Hadoop 的 NameNode 而設計的預寫日誌。它跨伺服器實例 bookies,在 ledgers 中提供消息的持久存儲。為了提供可恢復性,每個 bookie 都會同步地將每條消息寫入本地日誌,然後非同步地寫入其本地索引 ledger 存儲。與 Kafka 代理不同,bookie 之間不進行通信,BookKeeper 客戶端使用 quorum 風格的協議在 bookie 之間複製消息。

RabbitMQ 是一個開源的傳統消息中間件,它實現了 AMQP 消息標準,滿足了低延遲隊列用例的需求。RabbitMQ 包含一組代理進程,它們託管著發布消息的「交易所器」,以及從中消費消息的隊列。可用性和持久性是其提供的各種隊列類型的屬性。經典隊列提供的可用性保證最少。經典鏡像隊列將消息複製到其他代理並提高可用性。最近引入的Arbitrum隊列提供了更強的持久性,但是以性能為代價。由於這是一篇面向性能的博文,所以我們將評估限制在經典隊列和鏡像隊列。

2 分散式系統的持久性

單節點存儲系統(例如 RDBMS)依靠 fsync 寫磁碟來確保最大的持久性。但在分散式系統中,持久性通常來自複製,即數據的多個副本獨立失效。數據 fsync 只是在發生故障時減少故障影響的一種方法(例如,更頻繁地同步可能縮短恢復時間)。相反,如果有足夠多的副本失敗,那麼無論是否使用 fsync,分散式系統都可能無法使用。因此,我們是否使用 fsync 只是這樣一個問題,即每個系統選擇基於什麼方式來實現其複製設計。有些系統非常依賴於從不丟失寫入到磁碟的數據,每次寫入時都需要 fsync,但其他一些則是在其設計中處理這種情況。

Kafka 的複製協議經過精心設計,可以確保一致性和持久性,而無需通過跟蹤什麼已 fsync 到磁碟什麼未 fsync 到磁碟來實現同步 fsync。Kafka 假設更少,可以處理更大範圍的故障,比如文件系統級的損壞或意外的磁碟移除,並且不會想當然地認為尚不知道是否已 fsync 的數據是正確的。Kafka 還能夠利用操作系統批量寫入磁碟,以獲得更好的性能。

我們還不能十分確定,BookKeeper 是否在不 fsync 每個寫操作的情況下提供了相同的一致性保證——特別是在沒有同步磁碟持久化的情況下,它是否可以依賴複製來實現容錯。關於底層複製演算法的文檔或文章中沒有提及這一點。基於我們的觀察,以及 BookKeeper 實現了一個分組 fsync 演算法的事實,我們相信,它確實依賴於 fsync 每個寫操作來確保其正確性,但是,社區中可能有人比我們更清楚我們的結論是否正確,我們希望可以從他們那裡獲得反饋。

無論如何,由於這可能是一個有爭議的話題,所以我們分別給出了這兩種情況下的結果,以確保我們的測試儘可能的公平和完整,儘管運行帶有同步 fsync 功能的 Kafka 極其罕見,也是不必要的。

3 基準測試框架

對於任何基準測試,人們都想知道使用的是什麼框架以及它是否公平。為此,我們希望使用 OpenMessaging Benchmark Framework (OMB),該框架很大一部分最初是由 Pulsar 貢獻者編寫的。OMB 是一個很好的起點,它有基本的工作負載規範、測試結果指標收集 / 報告,它支持我們選擇的三種消息系統,它還有針對每個系統定製的模塊化雲部署工作流。但是需要注意,Kafka 和 RabbitMQ 實現確實存在一些顯著的缺陷,這些缺陷影響了這些測試的公平性和可再現性。最終的基準測試代碼,包括下面將要詳細介紹的修復程序,都是開源的。

OMB 框架修復

我們升級到 Java 11 和 Kafka 2.6、RabbitMQ 3.8.5 和 Pulsar 2.6 (撰寫本文時的最新版本)。藉助 Grafana/Prometheus 監控棧,我們顯著增強了跨這三個系統的監控能力,讓我們可以捕獲跨消息系統、JVM、Linux、磁碟、CPU 和網路的指標。這很關鍵,讓我們既能報告結果,又能解釋結果。我們增加了只針對生產者的測試和只針對消費者的測試,並支持生成 / 消耗積壓,同時修復了當主題數量小於生產者數量時生產者速率計算的一個重要 Bug。

OMB Kafka 驅動程序修復

我們修復了 Kafka 驅動程序中一個嚴重的 Bug,這個 Bug 讓 Kafka 生產者無法獲得 TCP 連接,存在每個工作者實例一個連接的瓶頸。與其他系統相比,這個補丁使得 Kafka 的數值更公平——也就是說,現在所有的系統都使用相同數量的 TCP 連接來與各自的代理通信。我們還修復了 Kafka 基準消費者驅動程序中的一個關鍵 Bug,即偏移量提交的過於頻繁及同步導致性能下跌,而其他系統是非同步執行的。我們還優化了 Kafka 消費者的 fetch-size 和複製線程,以消除在高吞吐量下獲取消息的瓶頸,並配置了與其他系統相當的代理。

OMB RabbitMQ 驅動程序修復

我們增強了 RabbitMQ 以使用路由鍵和可配置的交易所類型(DIRECT 交易所和 TOPIC 交易所),還修復了 RabbitMQ 集群設置部署工作流中的一個 Bug。路由鍵被引入用來模仿主題分區的概念,實現與 Kafka 和 Pulsar 相當的設置。我們為 RabbitMQ 部署添加了一個 TimeSync 工作流,以同步客戶端實例之間的時間,從而精確地測量端到端延遲。此外,我們還修復了 RabbitMQ 驅動程序中的另一個 Bug,以確保可以準確地測量端到端延遲。

OMB Pulsar 驅動程序修復

對於 OMB Pulsar 驅動程序,我們添加了為 Pulsar 生產者指定最大批次大小的功能,並關閉了那些在較高目標速率下、可能人為地限制跨分區生產者隊列吞吐量的全局限制。我們不需要對 Pulsar 基準驅動程序做任何其他重大的更改。

4 測試平台

OMB 包含基準測試的測試平台定義(實例類型和 JVM 配置)和工作負載驅動程序配置(生產者 / 消費者配置和伺服器端配置),我們將其用作測試的基礎。所有測試都部署了四個驅動工作負載的工作者實例,三個代理 / 伺服器實例,一個監視實例,以及一個可選的、供 Kafka 和 Pulsar 使用的三實例 Apache ZooKeeper 集群。在實驗了幾種實例類型之後,我們選定了網路 / 存儲經過優化的 Amazon EC2 實例,它具有足夠的 CPU 內核和網路帶寬來支持磁碟 I/O 密集型工作負載。在本文接下來的部分,我們會列出我們在不同的測試中對這些基線配置所做的更改。

磁碟

具體來說,我們選擇了 i3en.2xlarge(8 vCore,64GB RAM,2x 2500 GB NVMe SSD),我們看中了它高達 25 Gbps 的網路傳輸限額,可以確保測試設置不受網路限制。這意味著這些測試可以測出相應伺服器的最大性能指標,而不僅僅是網速多快。i3en.2xlarge 實例在兩塊磁碟上支持高達 約 655 MB/s 的寫吞吐量,這給伺服器帶來了很大的壓力。有關詳細信息,請參閱完整的 實例類型定義。根據一般建議和最初的 OMB 設置,Pulsar 把一個磁碟用於 journal,另一個用於 ledger 存儲。Kafka 和 RabbitMQ 的磁碟設置沒有變化。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 1:確定跨兩塊磁碟的 i3en.2xlarge 實例的最大磁碟帶寬,使用 Linux 命令 dd 進行測試,作為吞吐量測試的參考。

Disk 1dd if=/dev/zero of=/mnt/data-1/test bs=1M count=65536 oflag=direct65536+0 records in65536+0 records out68719476736 bytes (69 GB) copied, 210.278 s, 327 MB/sDisk 2dd if=/dev/zero of=/mnt/data-2/test bs=1M count=65536 oflag=direct65536+0 records in65536+0 records out68719476736 bytes (69 GB) copied, 209.594 s, 328 MB/s

OS 調優

此外,對於所比較的三個系統,為了獲得更好的延遲性能,我們使用 tune -adm 的延遲性能配置文件對操作系統進行了調優,它會禁用磁碟和網路調度器的任何動態調優機制,並使用性能調控器進行 CPU 頻率調優。它將每個內核的 p-state 固定在可能的最高頻率上,並將 I/O 調度器設置為 deadline,從而提供一個可預測的磁碟請求延遲上限。最後,它還優化內核中的電源管理服務質量(QoS),這是為了提高性能,而不是省電。

內存

與 OMB 中的默認實例相比,i3en.2xlarge 測試實例物理內存幾乎是前者的一半(64 GB vs. 122 GB)。優化 Kafka 和 RabbitMQ 使其與測試實例兼容非常簡單。兩者都主要依賴於操作系統的頁面緩存,隨著新實例的出現,頁面緩存會自動縮小。

然而,Pulsar 代理以及 BookKeeper bookie 都依賴於堆外 / 直接內存緩存,為了使這兩個獨立進程可以在 i3en.2xlarge 實例上良好地運行,我們調整了 JVM 堆 / 最大直接內存大小。具體來說,我們將堆大小從每個 24 GB (原始的 OMB 配置)減半為每個 12 GB,在兩個進程和操作系統之間按比例劃分了可用物理內存。

在測試中,當目標吞吐量比較高時,我們遇到了 java.lang.OutOfMemoryError: Direct buffer memory 錯誤,如果堆大小再低一點,就會導致 bookie 完全崩盤。這是使用堆外內存的系統所面臨的典型的內存調優問題。雖然直接位元組緩衝區是避免 Java GC 的一個有吸引力的選項,但是大規模使用是一個頗具挑戰性的做法。

5 吞吐量測試

我們開始測量的第一件事是,在網路、磁碟、CPU 和內存資源數量相同的情況下,每個系統可以實現的峰值穩定吞吐量。我們將穩定峰值吞吐量定義為消費者在不增加積壓的情況下可以跟得上的最高平均生產者吞吐量。

fsync 的效果

如前所述,Apache Kafka 的默認建議配置是使用底層操作系統指定的頁面緩存刷新策略(而不是同步地 fsync 每個消息) flush/fsync 到磁碟,並依賴複製來實現持久性。從根本上說,這提供了一種簡單而有效的方法來分攤 Kafka 生產者所使用的不同批次大小的成本,在各種情況下都可以實現最大可能的吞吐量。如果 Kafka 被配置為每次寫時 fsync,那麼我們就會因強制進行 fsync 系統調用而人為地妨礙了性能,並且沒有獲得任何額外的好處。

也就是說,考慮到我們將要討論這兩種情況的結果,我們仍然有必要了解在 Kafka 中每次寫時 fsync 的影響。各種生產者批次大小對 Kafka 吞吐量的影響如下所示。吞吐量隨著批次大小的增加而增加,直到到達「最佳點」,即批次大小足以讓底層磁碟完全飽和。在批次大小較大時,將 Kafka 上的每條消息 fsync 到磁碟(圖 2 中的橙色條)可以產生類似的結果。注意,這些結果僅在所述實驗平台的 SSD 上得到了驗證。Kafka 確實在所有批次大小上都充分利用了底層磁碟,在批次大小較小時最大化 IOPS,在批次大小較大時最大化磁碟吞吐量,甚至在強制 fsync 每條消息時也是如此。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 2:批次大小對 Kafka 吞吐量(每秒消息數)的影響,綠條表示 fsync=off (默認),橙條表示 fsync 每條消息

從上圖可以明顯看出,使用默認的 fsync 設置(綠條)可以讓 Kafka 代理更好地管理 page flush,從而提供更好的總體吞吐量。特別是,在生產者批次大小較小(1 KB 和 10 KB)時,使用默認同步設置的吞吐量比 fsync 每條消息的吞吐量高 3 到 5 倍。然而,批次較大(100 KB 和 1 MB)時,fsync 的成本被均攤了,吞吐量與默認 fsync 設置相當。

Pulsar 在生產者上實現了類似的批次,並在 bookie 間對產生的消息進行 quoro 風格的複製。BookKeeper bookie 在應用程序級實現分組提交 / 同步到磁碟,以最大化磁碟吞吐量。在默認情況下(由 bookie 配置 journalSyncData=true 控制),BookKeeper 會將寫入 fsync 到磁碟。

為了覆蓋所有的情況,我們測試 Pulsar 時在 BookKeeper 上設置了 journalSyncData=false,並與 Kafka 的默認(建議)設置(不對每條消息進行 fsync)進行了比較。但是,我們在 BookKeeper bookie 上遇到了大量延遲和不穩定性,表明存在與 flush 相關的隊列等待。我們還用 Pulsar 提供的工具 pulsar-perf 驗證到了同樣的行為。據我們所知,在諮詢了 Pulsar 社區後,這似乎是一個 Bug,所以我們選擇從我們的測試中排除它。儘管如此,考慮到我們可以看到磁碟在 journalSyncData=true 時吞吐量達到最大,我們相信它無論如何都不會影響最終結果。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 3:Pulsar 在 BookKeeper 設置了 journalSyncData=true 時,吞吐量明顯下跌,並且出現了延遲峰值

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 4:BookKeeper journal 回調隊列在 journalSyncData=false 設置下的增長情況

當且僅當消息尚未被消費時,RabbitMQ 會使用一個持久隊列將消息持久化到磁碟。然而,與 Kafka 和 Pulsar 不同,RabbitMQ 不支持「回放」隊列來再次讀取較舊的消息。從持久性的角度來看,在我們的基準測試中,消費者與生產者保持同步,因此,我們沒有注意到任何寫入磁碟的操作。我們還在一個三代理集群中使用了鏡像隊列,使 RabbitMQ 提供與 Kafka 和 Pulsar 相同的可用性保證。

測試設置

本實驗按照以下原則和預期保證進行設計:

  • 為了實現容錯,消息複製 3 份(具體配置見下文);

  • 為了優化吞吐量,我們啟用了所有三個系統的批處理。我們的批處理是 1MB 數據最多 10 毫秒;

  • 為 Pulsar 和 Kafka 的一個主題配置了 100 個分區;

  • RabbitMQ 不支持主題分區。為了匹配 Kafka 和 Pulsar 的設置,我們聲明了一個 direct exchange (相當於主題)和鏈接隊列(相當於分區)。關於這個設置的更多細節見下文。

OMB 使用一個自動速率發現演算法。該演算法通過以多個速率探測積壓來動態地獲取目標生產者的吞吐量。在許多情況下,我們看到速率在每秒 2 條消息到每秒 50 萬條消息之間劇烈波動。這嚴重影響了實驗的可重複性和準確性。在我們的實驗中,我們沒有使用該特性,而是顯式地配置了目標吞吐量,並按每秒 10K、50K、100K、200K、500K 和 100 萬條生產者消息的順序穩步提高目標吞吐量,四個生產者和四個消費者都使用 1 KB 的消息。然後,我們觀察了每個系統在不同配置下提供穩定端到端性能的最大速率。

吞吐量結果

我們發現,Kafka 在我們所比較的系統中吞吐量最高。考慮到它的設計,產生的每個位元組都只在一個編碼路徑上寫入磁碟一次,而這個編碼路徑已經被世界各地的數千個組織優化了近十年。我們將在下面更詳細地研究每個系統的這些結果。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 5:比較這三個系統的峰值穩定吞吐量:100 個主題分區,1 KB 消息,使用 4 個生產者和 4 個消費者

我們將 Kafka 配置為 batch.size=1MB 和 linger.ms=10,以便生產者可以有效地對發送給代理的寫操作進行批處理。此外,我們在生產者中配置了 acks=all 和 min.insync.replicas=2,確保在向生產者返回確認之前每條消息至少複製到兩個代理。我們發現,Kafka 能夠有效地最大限度地使用每個代理上的磁碟——這是存儲系統的理想結果。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 6:使用默認推薦 fsync 設置的 Kafka 性能。該圖顯示了 Kafka 代理上的 I/O 利用率和相應的生產者 / 消費者吞吐量(來源:Prometheus 節點指標)。

我們還採用另一種配置對 Kafka 進行了基準測試,即在確認寫操作之前使用 flush.messages=1 和 flush.ms=0 在所有副本上將每條消息 fsync 到磁碟。結果如下圖所示,非常接近默認配置。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 7:Prometheus 節點指標顯示 Kafka 代理上的 I/O 利用率以及相應的生產者 / 消費者吞吐量。

在生產請求排隊方面,Pulsar 的生產者與 Kafka 的工作方式不同。具體來說,它內部有每個分區的生產者隊列,以及對這些隊列的大小限制,對來自給定生產者的所有分區的消息數量設置了上限。為了避免 Pulsar 生產者在發送消息的數量上遇到瓶頸,我們將每個分區和全局限制均設置為無窮大,同時匹配基於 1 MB 位元組的批處理限制。

*
.batchingMaxBytes(1048576) // 1MB.batchingMaxMessages(Integer.MAX_VALUE).maxPendingMessagesAcrossPartitions(Integer.MAX_VALUE);

我們還為 Pulsar 提供了更高的基於時間的批處理限制,即 batchingMaxPublishDelayMs=50,以確保批處理主要是由位元組限制引起的。我們通過不斷增加這個值,直到它對 Pulsar 最終達到的峰值穩定吞吐量沒有可測量的影響。對於複製配置,我們使用了 ensemble blesize =3、writeQuorum=3、ackQuorum=2,這與 Kafka 的配置方式相當。在 BookKeeper 的設計中,bookie 將數據寫入 journal 和 ledger 中,我們注意到,峰值穩定吞吐量實際上是 Kafka 所能達到的吞吐量的一半。我們發現,這種基本的設計選擇對吞吐量有深遠的負面影響,直接影響了開銷。一旦 BookKeeper bookie 的 journal 磁碟完全飽和,Pulsar 的生產者速率就會被限制在那個點上。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 8:Prometheus 節點指標顯示了 BookKeeper journal 磁碟達到極限和最終在 BookKeeper bookie 上測得的吞吐量。

為了進一步驗證這一點,我們還配置 BookKeeper 在 RAID 0 配置 中使用兩個磁碟,這為 BookKeeper 提供了將 journal 和 ledger 寫操作分到兩個磁碟上的機會。我們觀察到,Pulsar 最大限度地利用了磁碟的聯合吞吐量(~650 MB/s),但峰值穩定吞吐量仍然限制在~340 MB/s。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 9:Prometheus 節點指標顯示,BookKeeper journal 磁碟在 RAID 0 配置下仍然達到極限

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 10:Prometheus 節點指標顯示,RAID 0 磁碟已達到極限,以及最終在 Pulsar 代理上測得的吞吐量。

Pulsar 有一個分層架構,將 BookKeeper bookie (存儲)與 Pulsar 代理(存儲的緩存 / 代理)分開。出於完整性考慮,我們也在分層部署中運行了上述吞吐量測試,將 Pulsar 代理移到了另外三個計算優化的 c5n.2xlarge 實例上(8 vCores, 21 GB RAM, Upto 25 Gbps network transfer, EBS-backed storage)。BookKeeper 節點仍在存儲優化的 i3en.2xlarge 實例上。這使得在這個特殊的設置中,Pulsar 和 BookKeeper 總共有 6 個實例 / 資源,比 Kafka 和 RabbitMQ 多了 2 倍的 CPU 資源和 33%的內存。

即使在高吞吐量下,系統也主要受到 I/O 限制,而且我們沒有發現這種設置帶來任何提升。該特定運行的完整結果見下表。事實上,Pulsar 的兩層架構似乎只是增加了開銷——兩個 JVM 佔用了更多的內存、兩倍的網路傳輸以及系統架構中更多的移動部件。我們預計,當網路受到限制時(不像我們的測試提供了過剩的網路帶寬),Pulsar 的兩層架構將以兩倍的速度耗盡網路資源,進而降低性能。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?

與 Kafka 和 Pulsar 不同的是,RabbitMQ 在主題中沒有分區的概念。相反,RabbitMQ 使用 exchange 將消息路由到鏈接隊列,使用頭屬性(header exchange)、路由鍵(direct 和 topic exchange)或綁定(fanout exchange),消費者可以從中處理消息。為了匹配工作負載的設置,我們聲明了一個 direct exchange (相當於主題)和鏈接隊列(相當於分區),每個隊列專用於為特定的路由鍵提供服務。端到端,我們讓所有生產者用所有路由鍵(輪詢)生成消息,讓消費者專門負責每個隊列。我們還按照社區建議的最佳實踐 優化了 RabbitMQ:

  • 啟用複製(將隊列複製到集群中的所有節點)

  • 禁用消息持久化(隊列僅在內存中)

  • 啟用消費者自動應答

  • 跨代理的負載均衡隊列

  • 24 個隊列,因為在 RabbitMQ 中,每個隊列使用一個專用的內核(8 個 vCPUx 3 個代理)

RabbitMQ 在複製開銷方面表現不佳,這嚴重降低了系統的吞吐量。我們注意到,在此工作負載期間,所有節點都是 CPU 密集型的(見下圖右側 y 軸綠線),幾乎沒有留出任何餘地來代理任何其他消息。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 11:RabbitMQ 吞吐量 + CPU 使用率。

6 延遲測試

考慮到流處理和事件驅動架構的日益流行,消息系統的另一個關鍵方面是消息從生產者穿過管道通過系統到達消費者的端到端延遲。我們設計了一個實驗,在每個系統都維持在最高穩定吞吐量而又沒有顯示出任何資源過度使用跡象的情況下,對所有三個系統進行比較。

為了優化延遲,我們更改了所有系統的生產者配置,將消息批處理時間設為最多僅為 1 毫秒(在吞吐量測試中是 10 毫秒),並讓每個系統保持默認推薦配置,同時確保高可用性。Kafka 被配置為使用其默認的 fsync 設置(即 fsync off), RabbitMQ 被配置為不持久化消但鏡像隊列。在反覆運行的基礎上,我們選擇在速率 200K 消息 / 秒或 200MB/s 下對比 Kafka 和 Pulsar,低於這個測試平台上單磁碟 300MB/s 的吞吐量限制。我們觀察到,當吞吐量超過 30K 消息 / 秒時,RabbitMQ 將面臨 CPU 瓶頸。

延遲結果

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 12:在 200K 消息 / 秒(Kafka 和 Pulsar,消息大小 1KB)和 30K 消息 / 秒(RabbitMQ,它不能承受更高的負載)速率下測得的配置為高可用標準模式的端到端延遲。註:延遲(毫秒)越低越好。

Kafka 的延遲始終比 Pulsar 更低。在這三個系統中,RabbitMQ 實現了最低的延遲,但考慮到其有限的垂直可擴展性,只是在吞吐量低很多的情況下才能提供。由於實驗的設置是有意的,所以對於每個系統,消費者總是能夠跟上生產者的速度,因此,幾乎所有的讀取都是從所有三個系統的緩存 / 內存中。

Kafka 的大部分性能可以歸因於做了大量優化的消費者讀取實現,它建立在高效的數據組織之上,沒有任何額外的開銷,比如數據跳過。Kafka 充分利用了 Linux 頁面緩存和零複製機制來避免將數據複製到用戶空間中。通常,許多系統(如資料庫)都構建了應用程序級緩存,為支持隨機讀 / 寫工作負載提供了更大的靈活性。無論如何,對於消息系統,依賴頁面緩存是一個很好的選擇,因為典型的工作負載執行順序讀 / 寫操作。Linux 內核經過多年的優化,能夠更智能地檢測這些模式,並使用預讀等技術來極大地提高讀取性能。類似地,構建在頁面緩存之上使得 Kafka 可以採用基於 sendfile 的網路傳輸,避免額外的數據複製。為了與吞吐量測試保持一致,我們還將 Kafka 配置為 fsync 每條消息然後運行了相同的測試。

Pulsar 採用了一種與 Kafka 非常不同的緩存方法,其中一些源於 BookKeeper 的核心設計選擇,即將 journal 和 ledger 存儲分開。除了 Linux 頁面緩存之外,Pulsar 還引入了多個緩存層,舉例來說,BookKeeper bookie 上的預讀緩存(我們的測試保留了 OMB 默認的 dbStorage_readAheadCacheMaxSizeMb = 1024),託管 ledger (managedLedgerCacheSizeMB,在我們的測試中是 20% 的可用直接內存,即 12GB*0.2 = 2.4GB)。在我們的測試中,我們沒有觀察到這種多層緩存的任何好處。事實上,多次緩存可能會增加部署的總體成本,我們懷疑,為了避免前面提到的使用直接位元組緩衝區帶來的 Java GC 問題,這 12GB 的堆外使用中存在大量的填充。

RabbitMQ 的性能取決於生產者端交易所和消費者端綁定到這些交易所的隊列。對於延遲實驗,我們使用了與吞吐量實驗相同的鏡像設置,特別是直接交易所和鏡像隊列。由於 CPU 瓶頸,我們無法驅動高於 38K 消息 / 秒的吞吐量,而且,基於這個速率度量延遲的任何嘗試都顯示出了性能的顯著下跌,p99 延遲幾乎達到了 2 秒。

逐漸將吞吐量從 38K 消息 / 秒降低到 30K 消息 / 秒,我們獲得了一個穩定的吞吐量,此時,系統資源似乎不存在過度利用。更好的 p99 延遲(1 毫秒)證實了這一點。我們認為,在吞吐量較高的情況下,在 3 個節點上複製 24 個隊列的開銷似乎對端到端延遲有嚴重的負面影響,而吞吐量小於 30K 消息 / 秒或 30MB/s (洋紅色實線)時,RabbitMQ 可以提供比其他兩個系統更低的端到端延遲。

通常,遵循最佳實踐,RabbitMQ 可以提供界限內延遲。鑒於實驗故意設置的延遲,消費者總是能跟上生產者,RabbitMQ 的消息管道效率歸根結底是 Erlang BEAM VM 處理隊列所需做的上下文切換的次數。因此,通過為每個 CPU 內核分配一個隊列來限制這一點可以提供最低的延遲。此外,使用 Direct 或 Topic 交易所允許對特定隊列進行複雜的路由(類似於 Kafka 和 Pulsar 上專用於分區的消費者)。但是,直接交易所提供了更好的性能,因為沒有通配符匹配,這會增加開銷,對這個測試來說,這是合適的選擇。

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 13:Kafka、Pulsar 和 RabbitMQ 的端到端延遲,測量時 Kafka 和 Pulsar 的速率為 200K 消息 / 秒(消息大小 1 KB),RabbitMQ 的速率為 30K 消息 / 秒。註:延遲(毫秒)越低越好。

在本節的開始,我們已經討論了 Kafka 在默認推薦 fsync 配置下的延遲結果(綠色實線)。在 Kafka 將每條消息 fsync 到磁碟(綠色虛線)的可選配置下,我們發現,Kafka 仍然比 Pulsar 延遲低,幾乎一直到 p99.9 百分位,而 Pulsar (藍色線)在更高的尾部百分位上表現更好。在 p99.9 及更高百分位上準確推斷尾部延遲非常困難,我們相信,在 Kafka fsync 可選配置(綠色虛線)下,考慮到生產者延遲似乎遵循相同的趨勢,p99.9 延遲的非線性暴漲可以歸因於涉及 Kafka 生產者的邊緣情況。

延遲權衡

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?圖 14:RabbitMQ 的端到端延遲:速率為 10K、20K、30K 和 40K 消息 / 秒時鏡像隊列(測試中使用的配置)與經典隊列對比(不複製)。註:在這個圖中,y 軸上的刻度是對數。

我們承認,每個系統在設計時都有一定的權衡。儘管對 Kafka 和 Pulsar 不公平,但我們發現,在不提供高可用性的配置下把 RabbitMQ 與 Kafka&Pulsar; 進行比較很有趣,後兩者都以較低的延遲為代價,提供了更強的持久性保證,並且可用性是 RabbitMQ 的三倍。對於某些用例而言(例如設備位置跟蹤),這可能很有意義。在這種情況下,用可用性來換取更好的性能是可以接受的,特別是當用例要求實時消息傳遞並且對可用性問題不敏感時。我們的結果表明,當禁用複製時,RabbitMQ 可以在更高的吞吐量下更好地保持較低的延遲,不過提高後的吞吐量(100K 消息 / 秒)仍然遠低於 Kafka 和 Pulsar 所能達到的水平。

儘管 Kafka 和 Pulsar 速度較慢(p99 百分位分別為大約 5 毫秒和 25 毫秒),但它們提供的持久性、更高的吞吐量和更高的可用性,對於處理金融事務或零售股票管理等大規模事件流用例來說至關重要。對於需要較低延遲的用例,在負載不重的情況下,RabbitMQ 可以實現大約 1 毫秒的 p99 百分位延遲,這是因為消息只是在內存中排隊,沒有複製開銷。

在實踐中,操作人員需要謹慎地配置 RabbitMQ,使速率足夠低,從而維持這樣的低延遲,否則延遲會迅速而顯著地退化。但是這個任務非常困難,實際上甚至不可能在所有用例中都以通用的方式實現。總的來說,一個比較好的、運營開銷和成本都比較低的架構可能會為所有用例都選擇一個像 Kafka 這樣的持久性系統,該系統可以在所有負載級別上以低延遲提供最好的吞吐量。

7 小結

在這篇博文中,我們對 Kafka、RabbitMQ 和 Pulsar 這三種消息系統進行了全面、均衡的分析,得出了以下結論:

  • 吞吐量:Kafka 在三個系統中的吞吐量最高,是 RabbitMQ 的 15 倍,Pulsar 的 2 倍。

  • 延遲:Kafka 在較高的吞吐量下提供了最低的延遲,同時還提供了強大的持久性和高可用性。在默認配置下,Kafka 在所有延遲基準測試中都要比 Pulsar 快,而且,當設置為 fsync 每條消息時,一直到 p99.9 百分位,它都更快。RabbitMQ 可以實現比 Kafka 更低的端到端延遲,但只能在吞吐量低很多的情況下。

  • 成本 / 複雜性:成本往往是性能的逆函數。作為具有最高穩定吞吐量的系統,由於其高效的設計,Kafka 提供了所有系統中最好的價值(即每位元組寫入成本)。事實上,Twitter 的 Kafka 之旅遠離了像 Pulsar 這樣的基於 BookKeeper 的架構,這證實了我們的觀察:Kafka 相對較少的移動部件顯著降低了它的成本(在 Twitter 的情況下高達 75%)。此外,將 ZooKeeper 從 Apache Kafka 中移除的工作正在進行中,這進一步簡化了 Kafka 的架構。

這篇博文的討論完全中心化在性能上,在比較分散式系統時還有更多的東西要討論。如果您有興趣了解更多關於 Kafka、Rabbit、Pulsar 和類似系統的分散式系統設計中那些有細微差別的權衡,請關注即將發布的新博文。

參考閱讀:

https://www.confluent.io/blog/kafka-fastest-messaging-system/

InfoQ 寫作平台歡迎所有熱愛技術、熱愛創作、熱愛分享的內容創作者入駐

還有更多超值活動等你來

掃描下方二維碼

填寫申請,成為作者

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?

開啟你的創作之路吧~

最新基準測試:Kafka、Pulsar 和 RabbitMQ 哪個最快?

點個在看少個 bug ?

來源鏈接:mp.weixin.qq.com

聲明:以上內容採集自互聯網,作品版權歸原創作者所有內容均以傳遞信息為目的,不代表本站同意其觀點,不作為任何投資指導。幣圈有風險,投資需謹慎
提示:投資有風險,入市需謹慎,本資訊不作為投資理財建議。請理性投資,切實提高風險防範意識;如有發現的違法犯罪線索,可積極向有關部門舉報反映。
你可能還喜歡