1. 程式人生 > >Apache Kafka基準測試:每秒寫入2百萬(在三臺廉價機器上)

Apache Kafka基準測試:每秒寫入2百萬(在三臺廉價機器上)

原文連結  譯者:astron  原文作者: Jay Kreps 2014年4月27日

我寫過一篇LinkedIn如何使用Apache Kafka作為集中釋出訂閱日誌,以便在應用程式,流處理和Hadoop之間整合資料的部落格文章。

kafka-logo-no-text

為了達到這樣的目的,這個“通用日誌”必須是一個簡單的抽象。如果要將系統用作中央資料中心,那麼它必須足夠快,可預測和易於擴充套件,以便將所有資料轉儲到其中。我的經驗是,脆弱或昂貴的系統不可避免地會形成一個阻礙人們無法使用它們的牆壁。一個容易擴充套件的系統通常最終會成為一個關鍵的架構構建模組,因為它構建起來最為簡單。

我一直喜歡Cassandra的基準測試,它在EC2和Google Compute Engine上的三百臺機器上每秒鐘寫入一百萬次。我不知道為什麼,也許這是一個Dr. Evil的事情,但每秒鐘做一百萬次任何事情都很有趣。

無論如何,正如我們將看到的那樣,kafka日誌的其中一個好處是它便宜。每秒百萬次寫入並不是特別大的事情。這是因為日誌比資料庫或鍵值儲存要簡單得多。事實上,我們的產品叢集全天要產生數千萬的讀寫,而且它們也是執行在廉價硬體上。

讓我們做一些基準測試並觀測結果。

30秒簡單回顧下kafka

為了幫助理解基準測試,讓我們快速回顧一下Kafka的內容以及有關它的工作原理的一些細節。 Kafka是一種分散式訊息系統,最初是在LinkedIn建立的,現在是Apache Software Foundation的一部分,並被各種公司使用。

一般配置很簡單。生產者將記錄傳送到叢集,將其記錄,並將其交給消費者:
producer_consumer

kafka的重要抽象是主題。生產者將記錄釋出到主題,消費者訂閱一個或多個主題。 Kafka主題只是一個分片上的的預寫日誌。生產者將記錄附加到這些日誌和消費者訂閱更改。每條記錄是一個鍵/值對。其中的key用於將記錄分配給特定日誌分割槽(除非釋出者直接指定分割槽)。

這是一個簡單的例子,單一的生產者和消費者從兩個分割槽主題的讀和寫。
partitioned_log_0

此圖顯示一個生產者程序追加到兩個分割槽的日誌,一個消費者從相同的日誌讀取。日誌中的每個記錄都有一個相關的條目號,我們稱之為偏移量。消費者使用偏移量來描述它在每個日誌中的位置。

這些分割槽分佈叢集上,允許主題容納比任何一臺機器上多的資料。

注意,與大多數訊息系統不同,日誌始終是持久的。當收到訊息時直接將其寫入檔案系統。訊息在讀取時不會被刪除,但按照可配置的SLA保留(比如說幾天或一週)。這允許在資料消費者可能需要重新載入資料的情況下使用。它還可以支援節省空間的釋出訂閱,因為無論消費者多少隻有單一共享日誌;在傳統的訊息系統中,通常每個消費者都有一個佇列,所以新增一個消費者可以使你的資料量翻倍。這使得Kafka非常適合於正常訊息系統外的事情,例如用作Hadoop等離線資料系統的管道。這些離線系統只能作為週期性ETL週期的一部分間隔載入,或者可能會花費幾個小時進行維護,在此期間,如果需要,Kafka能夠緩衝TB級別未消耗資料。

Kafka還通過複製日誌到多臺伺服器以進行容錯。與其他訊息傳遞系統相比,副本實現的一個重要架構是複製不需要複雜的配置,這些配置僅在非常特殊的情況下使用。假定複製是預設的:我們將未複製資料視為副本因子恰好為一個的特殊情況。

當生產者釋出包含偏移的訊息時,生產者會收到確認。釋出到分割槽的第一條記錄返回偏移量0,第二條記錄1,並按照序列增長。消費者從偏移指定的位置消費資料,並通過定期提交將位置儲存在日誌中:儲存該偏移量,以防消費者例項崩潰,另一個例項可以從偏移的位置恢復。

希望這些有所幫助(如果沒有,你可以在這裡閱讀更完整的kafka介紹)。

本次基準測試

這個測試是針對主幹,因為我對這個基準效能測試進行了一些改進。

自上個完全版本釋出以來,並無實質性變化,因此您應該看到與0.8.1相似的結果。我也使用我們新的重寫的Java生產者,比以前的生產者客戶端提供了更大的吞吐量。

我遵循了一個很好的RabbitMQ基準測試的基本模板,但也覆蓋了與Kafka更密切的場景和選項。

關於這個基準測試的哲學觀點。對於要公開的基準測試,我喜歡遵循“懶惰基準”的風格。當我們針對應用系統時,針對任何特定用例,通常可以將其調整到極致。這種風格基準測試,大量調整配置,甚至針對每個場景,都會有不同的調整。我認為一個系統的真實測試不是在引數調到極致後開始測試,而是如何執行手邊“現成”的系統。尤其對於在具有數十個或數百個用例的多租戶設定的系統來說,針對每個用例的調優不僅不切實際但不可能。因此,針對伺服器和客戶端,我大多使用預設配置引數。我會指出那些我懷疑可以通過微調得到結果改善的地方,但我試圖抵制誘惑,避免這種微調帶來的改善結果。

我釋出了我的準確配置和命令,所以使用你自己的機器也能復現實驗結果。

配置

我有六臺機器,每個都有以下規格的:

Xeon 2.5 GHz處理器六核

6個7200 RPM SATA驅動器

32GB的RAM

1Gb乙太網

Kafka叢集設定在三臺機器上。六個驅動器直接安裝,沒有RAID(JBOD風格)。剩下的三臺機器我用於Zookeeper並且用於產生負載。

三機叢集並不是很大,但是由於我們需要測試到三個副本因子,所以我們需要三臺機器。顯而易見的是,我們可以隨時新增更多的分割槽並將資料擴充套件到更多的機器上從而水平擴充套件我們的叢集。測試用的物理機器實際上不是LinkedIn的日常Kafka程式執行的硬體。我們日常使用的kafka機器針對kafka進行的調優,但與我的本意相反,我希望試圖在“現成”的通用機器上進行這些測試。相反,我從一個Hadoop叢集中借用到了這次實驗的機器,這些機器是我們任何持久化系統中最便宜的。 Hadoop與Kafka的使用模式非常相似,所以這是一件合理的事情。

閒話少說,看結果!

生產者吞吐量

對生產者的吞吐量進行壓力測試。測試期間沒有消費者執行,所以所有的訊息都被持久化但不會被讀取的(我們稍後會測試生產者和消費者都有的情況)。由於我們最近重寫了生產者,本次測試新的程式碼。

單個生產者執行緒,單副本

821557個記錄/秒(78.3 MB /秒)

對於這個第一個測試,我建立一個具有六個分割槽並且沒有副本的主題。然後,我從單個執行緒儘可能快地生成了五千萬個(100位元組)的小記錄。關注小記錄的原因在於通常對於訊息系統而言小記錄更難。如果訊息很大,很容易以MB /秒獲得良好的吞吐量,但是當訊息很小時,獲得良好的吞吐量更難,因為處理每個訊息的開銷占主導地位。

整個這個基準測試中,當我報告MB /秒,我只報告每秒請求記錄的個數,請求的其他開銷都不包括在內。所以實際的網路使用率會比報告的高。例如,使用100位元組的訊息,每條我們還將傳輸大約22位元組的開銷(對於可選的金鑰,分隔大小,訊息CRC,記錄偏移和屬性標誌)以及請求的一些開銷(包括主題,分割槽,確認等)。這使得我們到達網絡卡的極限有點困難,但這似乎更合理。因此,在上述結果中,我們可能會在客戶端機器上使用千兆網絡卡。

一個直觀結果是,資料遠遠高於人們期望的,特別是對於持久儲存系統。如果您習慣於隨機訪問資料系統(如資料庫或鍵值儲存),則通常期望最大吞吐量大約為每秒5,000到50,000個查詢,因為這接近好的RPC層可以執行遠端請求的速度。由於兩個關鍵的設計原則,我們超越了它們:

我們努力保證線性磁碟I / O。這些伺服器的六個廉價磁碟具有822 MB /秒的線性磁碟I / O的總吞吐量。這實際上遠遠超出了我們只能使用1千兆網絡卡。許多訊息系統將永續性作為一種嚴重降低效能的昂貴的附加功能,並認為應該謹慎使用,但這是因為它們不能做線性I / O。

在每個階段,我們都將小資料資料合併在一起,進行大型網路和磁碟I / O操作。例如,在新生產者中,我們使用“組提交”機制來確保任何記錄初始化傳送時另一個I / O正在進行分組。要了解更多批處理的重要性,請檢視David Patterson的演講文稿“Latency Lags Bandwidth”。

如果您對細節感興趣,您可以在我們的設計文件中閱讀更多內容。

單個生產者執行緒,三個副本,非同步方式

786980 record / sec(75.1 MB / sec)

此測試與前一個測試完全相同,只是每個分割槽都有三個副本(因此寫入網路或磁碟的總資料是三倍)。如果副本是master從生產者寫入分割槽,如果是follower則獲取和寫入資料到分割槽中。測試中的複製是非同步的。也就是說,伺服器一旦將訊息寫入本地日誌即可確認,而不必等待其他副本確認。這意味著,如果master宕機,它可能會丟失最後幾條已寫入但尚未複製的訊息。訊息確認延遲稍微好一些,而在伺服器故障的情況下,有副本丟失風險。

關鍵點是複製是快速的。總共叢集寫入容量是三倍(因為每條訊息寫入三次),但是每個客戶端的吞吐量仍然相當不錯。高效能複製在很大程度上來自於消費者的效率(複製真的只不過是一個特定功能的消費者)。我將在消費者部分討論。

單個生產者執行緒,3個副本,同步複製

428823條記錄/秒(40.2 MB /秒)

此測試與上述相同,只是現在master伺服器在確認回生產者前,要等待來自全部同步副本的確認。在這種模式下,只要一個同步的副本存在,我們保證訊息不會丟失。

在Kafka中,同步複製與從非同步複製其實並無太大區別。分割槽的leader總是跟蹤follower副本的進度來監控他們的活躍度,直到全部副本確認複製完畢,我們才向消費者發出訊息。通過同步複製,我們只需等待生產者請求的響應,直到follower複製完畢。

這個額外的延遲似乎影響了我們的吞吐量。由於伺服器上的程式碼路徑非常相似,我們可以通過將激進的批處理調整,允許客戶端緩衝更多未完成的請求來改善這種影響。然而,在避免特殊情況調優的原則下,我已經避免了這個測試。

三個生產者,3個副本,非同步複製

2024032個記錄/秒(193.0 MB /秒)

我們的單個生產者顯然不會壓測到三個節點的叢集。為了增加一點負載,我現在重複之前的非同步副本複製測試。但是現在使用執行在三臺不同機器上的三個生產者負載生成器(因為網絡卡瓶頸單機多程序不會有所幫助)。我們可以看看這三個生產者的總體吞吐量,以便更好地瞭解叢集的總容量。

生產者吞吐量與儲存資料

許多訊息系統的隱藏危險之一是,對於在記憶體中保留的資料,它們工作的很好。但當資料備份不消耗(因此需要儲存在磁碟上)時,它們的吞吐量下降一個數量級(或更多)。這意味著只要您的消費者保持訊息佇列及時清掉,事情就可以正常執行。但是一旦它們滯後,整個訊息層將備份未消耗的資料。備份導致資料進入磁碟,這反過來會導致效能下降到一個速率,這意味著訊息傳遞系統不能再跟上傳入的資料,並把它們備份或者直接掛掉。這是非常可怕的,因為在許多情況下,佇列的終極目的是優雅地處理這樣的情況。

由於Kafka對於未消費的訊息,總是保證O(1)的效能。

為了測試這個實驗,讓我們執行一段更長的時間,並在儲存的資料集增長時繪製結果:

throughput_vs_size_0

該圖實際上顯示了效能方面的變化,但跟資料大小沒有影響,寫入TB資料後跟最初寫入幾百MB效能一樣好。

這種差異是由於Linux的I / O管理設施批處理資料,然後定期重新整理。我們在Kafka生產叢集配置上設定得更好。http://kafka.apache.org/documentation.html#hwandos

消費者吞吐量

現在讓我們將注意力轉向消費者吞吐量。

注意,複製因子不會影響此測試的結果,因為消費者只能從一個副本讀取,無視副本數量。同樣,生產者的確認級別也不重要,因為消費者只讀取完全確認的訊息(即使生產者不等待完全確認)。這是為了確保消費者看到的任何訊息是在leader切換(如果當前leader失敗)之後。

單個消費者

90452條記錄/秒(89.7 MB /秒)

第一次測試,我們將在一個執行緒從我們的6分割槽3副本主題消費5000萬條訊息。

kafka的消費者是非常高效的。它通過從檔案系統直接獲取日誌塊來工作。它使用sendfile API直接通過作業系統傳輸,而無需通過應用程式複製此資料的開銷。這個測試實際上是從日誌頭部開始,所以它正在做真正的I / O讀取。然而,在生產環境中,消費者幾乎完全從作業系統頁面快取中讀取,因為它正在讀取剛剛由一些生產者寫入的資料(因此仍然被快取)。實際上,如果您在生產伺服器上執行I / O stat,看到即使消耗了大量資料,也沒有任何物理讀取。

對於期望的Kafka,消費者廉價是很重要的。一方面,副本自身就是消費者,所以讓消費者便宜,使得複製便宜。另外,這樣可以將資料處理變成一種廉價的操作,因此我們不需要緊緊控制可擴充套件性。

三個消費者

2615968個記錄/秒(249.5 MB /秒)

讓我們重複同樣的測試,但是執行三個並行的消費者程序,每個程序在不同的機器,並且消耗同一主題。

如預期的那樣,基本是線性擴充套件。(不奇怪,因為我們的模型中的消費者是如此簡單)。

生產者和消費者

795064記錄/秒(75.8 MB /秒)

上述測試只包括生產者和消費者獨立執行。現在我們來做更符合實際情況的事情,一起執行。實際上,在技術上已經這樣做了,因為我們的副本複製是把伺服器當作消費者來實現的。

我們來執行測試。對於此測試,我們將在六分割槽3副本主題上執行一個生產者和一個消費者,這些主題將開始為空。生產者使用非同步複製。報告的吞吐量是消費者吞吐量(生產者吞吐量的上限)。

正如我們預期的那樣,結果基與只有生產者的情況相同 – 消費者相當便宜。

訊息大小的影響/

之前報告的是100位元組的小訊息。較小的訊息是訊息系統更難的問題,因為它們會放大系統記錄的開銷。改變記錄大小,在按照條數/秒和MB /秒中繪製吞吐量來證明。

size_vs_record_throughput
正如預期,這個圖表顯示由於資料變大,我們每秒可以傳送的記錄的原始記錄條數減少。但是,如果在MB /秒圖中,隨著訊息變大,實際使用者資料的總位元組吞吐量增加:
size_vs_mb_throughput
使用10個位元組的訊息,實際上被CPU來限制,因為是獲取鎖和傳送訊息入隊操作,不能最大限度地發揮網路效能。然而,從100位元組開始,網路開始飽和(儘管MB / sec持續增加,因為固定大小的位元組與傳送總位元組佔比的越來越小)。

端到端延遲

2ms(中位數)

3毫秒(99百分位數)

14毫秒(99.9百分位數)

關於吞吐量,我們已經談到了很多,但是什麼是訊息傳遞的延遲?

也就是說,我們傳送給消費者的訊息需要多長時間?

對於這個測試,我們將建立生產者和消費者,並針對生產者向kafka叢集傳送訊息需要多長時間,被消費者接收進行多次計時。

注意,Kafka只有所有同步副本確認訊息時,才向消費者發出訊息。無論是使用同步還是非同步複製,這個測試將給出相同的結果。因為該設定隻影響生產者的確認。

重複測試

如果你想在自己的機器上嘗試這些基準測試,完全沒問題。正如我所說,我大多隻是使用我們與Kafka一起提供的預裝效能測試工具,並且主要使用伺服器和客戶端的預設配置。您也可以在此處檢視有關配置和命令的更多詳細資訊https://gist.github.com/jkreps/c7ddb4041ef62a900e6c。