Kafka: 用於日誌處理的分佈式消息系統

本文爲《Kafka: a Distributed Messaging System for Log Processing》的全文翻譯,爲了讓大家原汁原味的領悟到kafka的設計精髓,所以採用直譯並適當在括號中添加了作者觀點。
本文爲作者原創文章,轉載請保留出處和鏈接

摘要

日誌處理已成爲消費者互聯網公司數據通道的一個關鍵組成部分。我們介紹卡夫卡(Kafka),一種爲了在低延遲下收集和傳遞大容量的日誌數據而開發的分佈式消息系統。我們的系統融合了現有的日誌聚合器和消息系統的想法,適用於離線和在線消息消費。我們在Kafka做了一系列非常規但實用的設計來確保我們的系統有效性和擴展性。我們的實驗結果表明,Kafka在和另外兩款流行的消息系統的對比展現出了優秀的性能。我們已經在生產中使用Kafka已有一段時間了,它每天要處理數百GB的新數據。

概述

管理,性能,設計,實驗。

關鍵詞

消息,分佈式,日誌處理,吞吐率,在線

1. 引言

任何大型互聯網企業都會生成海量的日誌數據。這些數據通常與(1)用戶活動事件對應,如登錄,頁面瀏覽,點擊,點贊,分享,評論和搜索;(2)操作指標,例如服務調用棧,調用延遲,錯誤和系統指標(如每臺服務器上的CPU,內存,網絡或磁盤使用率)。日誌數據長期以來被當做一種分析組件去追蹤用戶粘度,系統使用率和其他指標。然而,互聯網應用的最新趨勢是直接在在站點中使用活動數據(一部分的生產數據)。這些包括(1)搜索關聯,(2)推薦由廣受歡迎的項目和事件驅動的內容(3)廣告跟蹤和報告,(4)防止和對抗濫用行爲的安全應用程序,例如垃圾郵件或未經授權的數據抓取,以及(5)聚合用戶狀態更新,供他們的功能“朋友”或“人脈”閱讀。

日誌數據的這種實時生產使用給數據系統帶來了新的挑戰,因爲其數據量比“實際”數據大幾個數量級。 例如,搜索,推薦和廣告通常需要精細的計算點擊率,這不僅會爲每個用戶點擊生成日誌記錄,還會爲每個頁面上數十個未被點擊的項目生成日誌記錄。 每天,中國移動收集5–8 TB的通話記錄[11],而Facebook收集近6 TB的各種用戶活動事件[12]。

許多用於處理此類數據的早期系統都依賴於從生產服務器上剝離日誌數據(scraping log files off production servers)來進行分析。許多用於處理此類數據的早期系統都依靠物理方式將日誌文件從生產服務器上刮下來進行分析。近年來,已經建立了幾種專門的分佈式日誌聚合器,包括Facebook的Scribe [6],Yahoo的Data Highway [4]和Cloudera的Flume [3]。這些系統主要用於收集日誌數據並將其加載日誌數據到數據倉庫或Hadoop [8](數據分析工具)中以供離線使用。在LinkedIn(一個社交網站)上,我們發現,除了上述傳統的離線分析,我們還需要以不超過幾秒鐘的延遲來支持大多數準實時應用程序。

我們已經建立了一種用於日誌處理的新型消息傳遞系統,稱爲Kafka [18],該系統結合了傳統日誌聚合器和消息系統的優勢。 一方面,Kafka是分佈式且可擴展的,並提供高吞吐量。 另一方面,Kafka提供類似於消息傳遞系統的API,並允許應用程序實時消費(consume)日誌事件。 Kafka已開源,並已在LinkedIn上成功用於生產中超過6個月。它極大地簡化了我們的服務架構,因爲我們可以利用單個軟件來消費所有類型的在線和離線數據,所以。本文的其餘部分如下。 我們將在第2節中回顧傳統的消息傳遞系統和日誌聚合器。在第3節中,我們描述Kafka的體系結構及其關鍵設計原則。我們將在第4節中介紹在LinkedIn上部署Kafka的情況,並在第5節中介紹Kafka的性能結果。在第6節中,我們討論未來的工作並得出結論。

2. 相關工作

傳統的企業級消息傳遞系統[1] [7] [15] [17]已經存在了很長時間,並且總是在處理異步數據流的事件總線發揮關鍵作用。但是,由於某些原因,它們往往不適合日誌處理。首先,有一些不匹配的特徵被提供給這些企業級系統。這些系統通常專注於提供豐富的傳遞保證(delivery guarantees, 這裏可以理解爲傳遞特性)。例如,IBM Websphere MQ [7]支持事務,允許應用程序原子地將消息插入多個隊列。 JMS [14]規範允許在每條消息被消費後才確認(譯者注: 消息消費後才提交消費位移),這可能會導致亂序消費(譯者注: 分佈式系統裏面並行消費消息不能保證消費的順序)。這樣的交付保證對於收集日誌數據通常是過大的。例如,偶爾失去一些綜合瀏覽量事件肯定不是世界末日。這些不需要的功能往往會增加API和這些系統的基礎實現的複雜性。其次,許多系統沒有極力的專注於吞吐量,把它主要的設計約束。例如,JMS沒有API允許生產者將多個消息顯式批處理到一個單個請求。這意味着每個消息都需要完整的TCP / IP往返,這對於我們領域的吞吐量要求是不可行的。第三,這些系統缺乏分佈式支持。沒有簡單的方法可以在多臺計算機上分區和存儲消息。最後,許多消息傳遞系統假定消息將立即消耗掉,因此未消費消息的隊列總是很小。如果允許堆積消息,則它們的性能將大大降低,對於離線消費者不合適(例如,數據倉庫會定期進行高負載而不是連續消費)。

在過去的幾年中,已經建立了許多專業的日誌聚合器。 Facebook使用名爲Scribe的系統。每個前端計算機都可以通過套接字將日誌數據發送到一組Scribe服務器。每臺Scribe機器都會彙總日誌條目,並將它們定期轉儲(periodically dumps)到HDFS [9]或NFS設備。雅虎的數據高速公路項目具有類似的數據流。一組計算機聚集來自客戶端的事件並獲得(roll out)“分鐘”文件,然後將其添加到HDFS。 Flume是由Cloudera開發的相對較新的日誌聚合器。它支持可擴展的“管道”和“接收器”,並使流式日誌數據非常靈活。它還具有更多集成的分佈式支持。但是,大多數這些系統是爲脫機使用日誌數據而構建的,並且經常不必要地向消費者(譯者注: 原文是consumer,指的是消息消費方)公開實現細節(例如“分鐘文件”)。此外,它們中的大多數使用“推”模型把數據從broker轉發給消費者。在LinkedIn,我們發現“拉”模型更適合我們的應用程序,因爲每個消費者都可以以其可以維持的最大速率取回(retrieve)消息,並避免推送速度超出其處理能力。拉模型還可以輕鬆地倒帶(指重複消費),我們將在第3.2節的末尾討論這種好處。最近,雅虎!研究開發了一種新的分佈式發佈/訂閱系統,稱爲HedWig [13]。 HedWig具有高度的可擴展性和可用性,並提供強大的高可用(strong durability)保證。然後,它主要用於存儲數據存儲的提交日誌。

3.卡夫卡架構與設計原則

由於現有系統的限制,我們開發了一種新的基於消息的日誌聚合器Kafka。我們首先介紹Kafka中的基本概念。一個特定類型的消息流被定義爲一個主題。生產者可以將消息發佈到主題。 然後,已發佈的消息將存儲在一組稱爲broker的服務器上。 消費者可以訂閱來自brokers的一個或多個主題,並通過從brokers上拉數據來消費訂閱的消息。

消息從概念上講很簡單,我們試圖使Kafka API同樣簡單以反映這一點。 我們沒有顯示確切的API,而是提供了一些示例代碼來說明如何使用API。 生產者的示例代碼如下。 一條消息被定義爲僅包含有效負載(payload)的字節。用戶可以選擇她喜歡的序列化方法來編碼消息。 爲了提高效率,生產者可以在單個發佈請求中發送一組消息(批量發送)。

發送代碼樣例:

producer = new Producer(…);
message = new Message(“test message str”.getBytes()); 
set = new MessageSet(message);
producer.send(“topic1”, set);

爲了訂閱主題,消費者首先爲該主題創建一個或多個消息流。 發佈給該主題的消息將均勻分配到這些子流中。有關Kafka如何分發消息的詳細信息,將在第3.2節中介紹。每個消息流爲不斷產生的消息上提供迭代器接口。然後,消費者迭代流中的每個消息並處理消息的有效負載。與傳統的迭代器不同,消息流迭代器永遠不會終止。如果當前沒有更多消息可使用,則迭代器將阻塞,直到將新消息發佈到該主題爲止。我們既支持多個消費者共同使用一個主題中所有消息的單個副本的點對點傳遞模型,也支持多個消費者各自檢索其自己的主題副本的發佈/訂閱模型。

消費代碼樣例:

streams[] = Consumer.createMessageStreams(“topic1”, 1);
for (message : streams[0]) {
bytes = message.payload();
// do something with the bytes
}

Kafka的總體結構如圖1所示。由於Kafka實際上是分佈式的,因此一個Kafka集羣通常由多個代理(brokers)組成。爲了負載均衡,一個主題被分爲多個分區,每個代理存儲一個或多個這些分區。多個生產者和消費者可以同時發佈和取回消息。 在第3.1節中,我們描述了代理上單個分區的佈局以及我們選擇的一些設計選擇,以使訪問分區更加有效。在3.2節中,我們描述了生產者和消費者如何在分佈式環境中與多個代理進行交互。 我們將在第3.3節中討論Kafka的傳輸保障。

圖1. Kafka架構
圖1. Kafka架構

3.1單分區有效性

我們在Kafka上做出了一些決策來提高系統效率。

簡單存儲:Kafka的存儲佈局非常簡單。主題的每個分區都對應一個邏輯日誌。在物理上,日誌是由一組大小近似相同(例如1GB)的段文件實現的。每次生產者將消息發佈到分區時,代理都將消息簡單地附加到最後一個段文件。爲了獲得更好的性能,我們僅在發佈了可配置數量的消息或經過一定時間後纔將分段文件刷新到磁盤。消息僅在刷新後才暴露(exposed)給消費者。(譯者注:其實對應了kafka刷盤時候的兩個參數,超過一定數量,超過一定時間觸發刷盤策略,其實網絡IO發送接收數據也是這樣,提供緩衝來提高性能)
與典型的郵件系統不同,存儲在Kafka中的郵件沒有顯式(explicit)的消息ID。而是每個消息都通過其在日誌中的邏輯偏移量(offset)來尋址。這樣避免了維護將消息ID映射到實際消息位置的過程中,密集的隨機訪問索引產生的開銷。請注意,我們的消息ID正在增加,但不是連續的。要計算下一條消息的ID,我們必須將當前消息的長度添加到其ID中。從現在開始,我們將交替使用消息ID和偏移量。

消費者始終按順序使用來自特定分區的消息。如果消費者確認特定的消息偏移量,則表示消費者已經在分區中的該偏移量之前接收了所有消息。在這個過程的背後,消費者正在向代理髮送異步請求,以使數據緩衝區可供應用程序使用。每個拉取請求都包含從中開始使用的消息的偏移量和要提取的可接受的字節數(譯者注: 這裏可以參考消息的結構)。每個代理將內存中的偏移量列表排序,包括每個段文件中第一條消息的偏移量。代理通過搜索偏移量列表來定位所請求消息所保存的段文件,並將數據發送回消費者。在消費者接收到一條消息後,它將計算下一條要使用的消息的偏移量,並在下一個請求請求中使用它(譯者注:這裏指的是提交消費位移,是當前消費位移+1)。Kafka日誌和內存索引的佈局如圖2所示。每個框顯示一條消息的偏移量。
圖2 卡夫卡日誌結構
圖2 卡夫卡日誌結構

高效傳輸:我們非常謹慎地將數據傳入和傳出Kafka。之前,我們已經展示了生產者可以在單個發送請求中提交一組消息。儘管最終用戶API一次迭代一條消息,但在幕後,來自用戶的每個拉取請求還可以取回(retrieves)到一定大小(通常爲數百KB)的多條消息。

我們做出的另一個非常規選擇是避免在Kafka層上將消息顯式緩存在內存中。相反,我們依賴於基礎文件系統頁面緩存(pagecache)。這避免了雙重緩衝消息。這樣做還有一個好處,即使重新啓動代理進程,也可以保留熱緩存。由於Kafka根本不緩存進程中的消息,因此在垃圾回收(GC)方面幾乎沒有開銷,因此可以使用基於VM的語言進行高效實現。最後,由於生產者和消費者都可以順序地訪問段文件,由於消費者通常落後於生產者少量,因此正常的操作系統緩存試探法非常有效(特別是直寫式緩存和預讀)。我們已經發現,生產和消耗都具有與數據大小成線性關係的一致性能,最大可達數TB的數據。

此外,我們爲消費者優化了網絡訪問。 Kafka是一個多用戶系統,單個消息可能會被不同的消費者應用程序多次使用。從本地文件向遠程套接字發送字節的典型方法包括以下步驟:(1)從存儲介質讀取數據到OS中的頁面緩存,(2)將頁面緩存中的數據複製到應用程序緩衝區,(3)將應用程序緩衝區複製到另一個內核緩衝區,(4)將內核緩衝區發送到套接字。這包括4個數據複製和2個系統調用。在Linux和其他Unix操作系統上,存在一個sendfile API [5],它可以直接將字節從文件通道傳輸到套接字通道。這樣通常可以避免步驟2和步驟3中引入2個副本和1個系統調用。 Kafka利用sendfile API有效地將日誌段文件中的字節從代理傳遞到消費者。(譯者注:這裏用了零拷貝技術,避免了拷貝過程中無意義的數據複製和Linux系統中內核態和用戶態的切換)

無狀態代理:與大多數其他消息傳遞系統不同,在Kafka中,有關每個消費者已消費多少的信息不是由代理維護的,而是由消費者自己維護的。這樣的設計減少了代理的很多複雜性和開銷。但是,由於代理不知道是否所有訂閱者都已消費了消息,因此刪除變得很棘手(tricky)。 Kafka通過將簡單的基於時間的SLA用於保留策略來解決此問題。如果消息在代理中的保留時間超過一定時間(通常爲7天),則會自動刪除該消息。 該解決方案在實踐中效果很好。 無論大多數消費者(包括離線消費者)每天,每小時或實時完成消費。Kafka的性能不會隨着數據量的增加而降低,這一事實使得這種長期保留成爲可能。

此設計有一個重要的附帶好處。 消費者可以有意地倒回舊的偏移量並重新使用數據。 這違反了隊列的通用約定,但是事實證明這是許多消費者的基本功能。 例如,當消費者中的應用程序邏輯出現錯誤時,錯誤修復後,應用程序可以重播某些消息。 這對於將ETL數據加載到我們的數據倉庫或Hadoop系統中特別重要。 另一個例子是,所消費的數據可以僅週期性地被刷新到持久性存儲(例如,全文本索引器)。如果消費者崩潰了,未清除的數據將會丟失。(譯者注:kafka的leader刷盤成功就返回的話,ISR副本沒有fetch數據的情況下,如果leader硬盤壞丟數據,數據會產生少量丟失)在這種情況下,消費者可以檢查未清除消息的最小偏移量,並在重新啓動後從該偏移量中重新使用。我們注意到,在拉模型中支持消費者重複消費要比推模型容易得多。

3.2分佈式協調

現在我們描述生產者和消費者在分佈式環境中的行爲。每個生產者可以將消息發佈到隨機選擇的分區或由分區鍵和分區函數(譯者注:這裏和rabbitmq的路由鍵不一樣)語義確定的分區。我們將關注消費者與代理的互動方式。

Kafka具有消費羣體的概念。每個消費者組由一個或多個共同組成一組訂閱主題的消費者組成,即,每個消息僅傳遞給該組中的一個消費者。(譯者注:集羣消費)不同的消費者組各自獨立地消費整個訂閱消息集,並且不需要跨消費者組進行協調。同一組中的消費者可以處理不同的消息,或者在不同的機器上。我們的目標是在消費者之間平均分配存儲在代理中的消息,而不會引入過多的協調開銷。(譯者注:消費者負載均衡策略)

我們的第一個決定是使主題內的分區成爲並行度的最小單位。這意味着在任何給定時間,來自一個分區的所有消息僅由每個消費者組中的單個消費者使用。如果我們允許多個消費者同時使用一個分區,那麼他們將必須協調誰使用哪些消息,這需要鎖定和狀態維護開銷。相反,在我們的設計中,消耗過程僅在消耗者重新平衡負載時才需要協調,這種情況很少發生。爲了使負載真正達到平衡,我們在一個主題中需要的分區要比每組中的消費者多得多。我們可以通過對主題進行過度劃分來輕鬆實現這一目標。(譯者注:隊列輕量化)
我們做出的第二個決定是沒有中央的“主”節點,而是讓消費者以分散的方式在彼此之間進行協調。添加主服務器會使系統複雜化,因爲我們必須進一步擔心主服務器故障。爲了促進協調,我們使用了高度可用的共識服務Zookeeper [10]。 Zookeeper有一個非常簡單的文件系統,例如API。可以創建路徑,設置路徑的值,讀取路徑的值,刪除路徑和列出路徑的子路徑。它做一些更有趣的事情:(a)可以在路徑上註冊觀察者(watcher),並在路徑的子代或路徑的值發生更改時得到通知; (b)可以臨時創建一條路徑(與持久性相反),這意味着如果創建的客戶端不存在,則該路徑將由Zookeeper服務器自動刪除; (c)zookeeper將其數據複製到多個服務器,這使數據高度可靠且可用。

Kafka使用Zookeeper執行以下任務:(1)檢測代理和消費者的添加和刪除,(2)在上述事件發生時在每個消費者中觸發重新平衡過程,以及(3)維護消費關係並跟蹤每個分區的消耗的偏移量。具體來說,每個代理或消費者啓動時,會將其信息存儲在Zookeeper中的代理或消費者註冊表中。代理註冊表包含代理的主機名和端口,以及存儲在其中的主題和分區集。消費者註冊表包括消費者所屬的消費者組及其訂閱的主題集。每個消費者組都與Zookeeper中的所有權註冊表和偏移量註冊表關聯。註冊表對每個訂閱的分區都有一個路徑,路徑值是該分區當前使用的消費者的ID(我們使用消費者擁有該分區的術語)。偏移量註冊表爲每個訂閱的分區存儲該分區中最後消耗的消息的偏移量。(譯者注:kafka爲每個分區在zk上註冊對應的數據並管理偏移量)

在Zookeeper中創建的路徑對於代理註冊表,消費者註冊表和所有權註冊表是臨時的,對於偏移量註冊表是持久的。如果代理失效,則該代理上的所有分區都會自動從代理註冊表中刪除。消費者的故障導致它丟失在消費者註冊表中的條目以及在所有者註冊表中擁有的所有分區。每個消費者都在代理註冊表和消費者註冊表上都註冊了Zookeeper監視程序,並且每當代理集或消費者組發生更改時,都將收到通知。(譯者注:故障轉移策略,kafka在0.8.x之後引入了controller來觀察zk,避免了集羣行爲不一致,即腦裂)

在消費者的初始啓動過程中,或者通過觀察者通知消費者有關代理/消費者更改的消息時,消費者將啓動重新平衡過程以確定應從中使用的新分區子集。算法1中描述了該過程。通過從Zookeeper中讀取代理和消費者註冊表,消費者首先計算可用於每個已訂閱主題T的分區集(PT)和訂閱T的消費者集(CT)。對於消費者選擇的每個分區,它都會在偏移量註冊表將自己寫爲該分區的新所有者。最後,消費者開始一個線程,從偏移註冊表中存儲的偏移開始,從每個擁有的分區中提取數據。隨着從分區中提取消息,消費者將定期更新偏移量註冊表中的最新消耗的偏移量。(譯者注:消費者上下線引發負載均衡)

當一個組中有多個消費者時,將通知每個代理或消費者變更。但是,在消費者處通知的發出時間可能略有不同。因此,一個消費者有可能嘗試獲得仍由另一個消費者擁有的分區的所有權。發生這種情況時,第一個消費者只需釋放其當前擁有的所有分區,稍等片刻,然後重試重新平衡過程。實際上,重新平衡過程通常只需重試幾次即可穩定下來。(譯者注:隊列的所有者切換)

創建新的消費者組時,偏移量註冊表中沒有可用的偏移量。在這種情況下,使用我們在代理程序上提供的API,使用方將從每個訂閱分區上可用的最小或最大偏移量(取決於配置)開始。

3.3交付保證

通常,Kafka僅保證最少一次交付(at least once)。僅一次交付通常需要兩階段提交,而對於我們的應用程序則不是必需的。在大多數情況下,一條消息僅一次發送給每個消費者組。但是,如果消費者進程崩潰而沒有完全關閉,則接管有故障的消費者擁有的那些分區的消費者進程可能會得到一些重複的消息,這些消息是在最後一次偏移成功提交給Zookeeper之後的。如果應用程序關心重複項,則它必須使用我們返回給消費者的偏移量或消息中的某些唯一鍵來添加自己的重複數據刪除邏輯。與使用兩階段提交相比,這通常是一種更具成本效益的方法。(譯者注:目前大部分應用都是通過flink或者db來保證精確一次)
Kafka保證將來自單個分區的消息按順序傳遞給消費者。但是,不能保證來自不同分區的消息的順序。
爲了避免日誌損壞,Kafka將每個消息的CRC存儲在日誌中。如果代理上存在任何I / O錯誤,Kafka將運行恢復過程以刪除帶有不一致CRC的消息。在消息級別使用CRC還可以使我們在產生或使用消息之後檢查網絡錯誤。

如果代理髮生故障,則存儲在其上尚未使用的任何消息將變得不可用。如果代理上的存儲系統被永久損壞,則所有未使用的消息將永遠丟失。將來,我們計劃在Kafka中添加內置複製,以將每個消息冗餘地存儲在多個代理上。

4. LinkedIn上的Kafka的使用

在本節中,我們描述瞭如何在LinkedIn使用Kafka。圖3顯示了我們部署的簡化版本。我們有一個Kafka集羣與每個運行面向用戶服務的數據中心共置一處。前端服務生成各種日誌數據,並將其分批發布到本地Kafka代理。我們依靠硬件負載平衡器將發佈請求平均分配給Kafka brokers。 Kafka的在線消費者在同一數據中心內的服務中運行。
在這裏插入圖片描述
我們還在單獨的數據中心中部署了一個Kafka集羣,以進行離線分析,該集羣的地理位置靠近我們的Hadoop集羣和其他數據倉庫基礎架構。該Kafka實例運行一組嵌入式(embedded)消費者,以從實時數據中心的Kafka實例中提取數據。然後,我們運行數據加載作業,以將數據從Kafka的副本集羣中拉入Hadoop和我們的數據倉庫,在此我們對數據運行各種報告作業和分析過程。我們還使用此Kafka集羣進行原型製作,並能夠針對原始事件流運行簡單腳本以進行臨時查詢。無需太多調整,整個管道的端到端延遲平均約爲10秒,足以滿足我們的要求。

目前,Kafka每天存儲數百GB的數據和近10億條消息,隨着我們完成對舊系統的轉換以利用Kafka的優勢,我們預計這一數字將顯着增長。將來會添加更多類型的消息。當操作人員啓動或停止代理進行軟件或硬件維護時的重新平衡過程能夠自動重定向。(譯者注:這裏原文寫的有點彆扭,應該是說集羣可以自己遷移分區,實現整個集羣的負載均衡)

我們的跟蹤還包括一個審計系統,以驗證整個管道中沒有數據丟失。爲方便起見,每條消息在生成時均帶有時間戳和服務器名稱。我們對每個生產者進行檢測,使其定期生成監視事件,該事件記錄該生產者在固定時間窗口內針對每個主題發佈的消息數。生產者在單獨的主題中將監視事件發佈到Kafka。然後,消費者可以計算他們從給定主題中收到的消息數,並使用監視事件來驗證這些計數以驗證數據的正確性。(譯者注:正確性驗證其實我自己在學的時候沒有想到)

加載到Hadoop集羣中是通過實現一種特殊的Kafka輸入格式來完成的,該格式允許MapReduce作業直接從Kafka讀取數據。MapReduce作業將加載原始數據,然後對其進行分組和壓縮,以在將來進行有效處理。消息偏移的無狀態代理和客戶端存儲再次在這裏發揮作用,允許MapReduce任務管理(允許任務失敗並重新啓動)以自然方式處理數據加載,而不會在出現以下情況時複製或丟失消息任務重新啓動。僅在成功完成作業時,數據和偏移量都存儲在HDFS中。

我們選擇使用Avro [2]作爲序列化協議,因爲它高效且支持架構演變。對於每條消息,我們將其Avro模式的ID和序列化的字節存儲在有效負載中。這種模式使我們可以執行合約(enforce a contract),以確保數據生產者和消費者之間的兼容性。我們使用輕量級架構註冊表服務將架構ID映射到實際架構。消費者收到消息時,它將在模式註冊表中進行查詢以檢索模式,該模式用於將字節解碼爲對象(由於值是不可變的,因此每個模式只需要執行一次該查詢)。(譯者注:即反序列化)

5.實驗結果

我們進行了一項實驗研究,將Kafka與Apache ActiveMQ v5.4 [1](一種流行的JMS開源實現)和RabbitMQ v2.4 [16](一種以其性能着稱的消息系統)的性能進行了比較。我們使用了ActiveMQ的默認持久消息存儲KahaDB。儘管此處未介紹,但我們還測試了替代的AMQ消息存儲,發現其性能與KahaDB非常相似。只要有可能,我們都會嘗試在所有系統中使用可比較的設置。

我們在2臺Linux機器上進行了實驗,每臺機器具有8個2GHz內核,16GB內存,具有RAID 10的6個磁盤。這兩臺機器通過1Gb網絡鏈路連接。其中一臺機器用作中間人,另一臺機器用作生產者或消費者。

生產者測試:我們將所有系統中的代理配置爲異步將消息刷新到其持久性存儲。對於每個系統,我們運行一個生產者來發布總共1000萬條消息,每條消息200個字節。我們配置了Kafka生產者以1和50的大小批量發送消息。ActiveMQ和RabbitMQ似乎沒有簡單的方法來批量發送消息,並且我們假定它使用的批量大小爲1。結果如圖4所示。x軸表示隨時間推移發送給代理的數據量(以MB爲單位),y軸表示生產者吞吐量(以每秒消息數爲單位)。平均而言,Kafka可以以每秒50,000和400,000條消息的速度發佈消息,批量大小分別爲1和50。這些數字比ActiveMQ高几個數量級,比RabbitMQ高至少2倍。
圖4 生產者性能
圖4 生產者性能

卡夫卡表現出色的原因有很多。首先,Kafka生產者當前不等待代理人的確認,而是以代理人可以處理的最快速度發送消息。這大大提高了發佈者的吞吐量。批量大小爲50,單個Kafka生產商幾乎飽和了生產商和代理之間的1Gb鏈路。對於日誌聚合情況,這是一種有效的優化,因爲必須異步發送數據,以避免將任何等待時間引入流量的實時服務中。我們注意到,在沒有確認生產者的情況下,不能保證代理實際上收到了每個已發佈的消息。對於許多類型的日誌數據,希望以持久性換取吞吐量,只要所丟棄消息的數量相對較小即可。但是,我們確實計劃在將來解決持久性問題,以獲取更多關鍵數據。

其次,Kafka具有更有效的存儲格式。平均而言,每條消息在Kafka中的開銷爲9字節,而在ActiveMQ中爲144字節。這意味着ActiveMQ使用的存儲空間比Kafka多70%,用於存儲相同的1000萬條消息。 ActiveMQ的一項開銷來自JMS所需的繁重消息頭。另一個開銷是維護各種索引結構的成本。我們觀察到ActiveMQ中最繁忙的線程之一花費了大部分時間來訪問B樹以維護消息元數據和狀態。(譯者注:kafka各種操作都是接近O(1))最後,批處理通過攤銷(原文是amortizing)RPC開銷大大提高了吞吐量。在Kafka中,每批50條消息將吞吐量提高了近一個數量級。

消費者測試:在第二個實驗中,我們測試了消費者的表現。同樣,對於所有系統,我們僅使用一個消費者就可以檢索總共1000萬條消息。我們配置了所有系統,以便每個拉取請求應預取大約相同數量的數據-最多1000條消息或大約200KB。對於ActiveMQ和RabbitMQ,我們將消費者確認模式設置爲自動。由於所有消息都適合內存,因此所有系統都在提供來自底層文件系統的頁面緩存或某些內存緩衝區中的數據。結果如圖5所示。
在這裏插入圖片描述
圖5.消費者表現

平均而言,Kafka每秒消耗22,000條消息,是ActiveMQ和RabbitMQ的4倍以上。我們可以想到幾個原因。首先,由於Kafka具有更有效的存儲格式,因此將更少的字節從代理傳輸到卡夫卡的消費者。其次,ActiveMQ和RabbitMQ中的代理都必須維護每個消息的傳遞狀態。我們觀察到,在此測試期間ActiveMQ線程之一正在忙於將KahaDB頁面寫入磁盤。相反,Kafka代理上沒有磁盤寫入活動。最後,通過使用sendfile API,Kafka減少了傳輸開銷。

在結束本節時,我們注意到該實驗的目的並不是要表明其他消息傳遞系統不如Kafka。畢竟,ActiveMQ和RabbitMQ都具有比Kafka更多的功能。重點是說明通過專用系統可以實現的潛在性能提升。

6. 總結與展望

我們提出了一種稱爲Kafka的新型系統,用於處理大量日誌數據流。像消息傳遞系統一樣,Kafka使用基於拉的使用模型,該模型允許應用程序以自己的速率使用數據並在需要時重複消費。通過專注於日誌處理應用程序,Kafka實現了比常規消息傳遞系統更高的吞吐量。它還提供集成的分佈式支持,並且可以擴展。我們已經在LinkedIn上成功地將Kafka用於離線和在線應用程序。

我們將來會遵循許多方向。首先,我們計劃在多個代理之間添加消息的內置複製,即使在無法恢復的機器故障的情況下,也可以保證持久性和數據可用性。我們希望同時支持異步和同步複製模型,以在生產者延遲和提供的保證強度之間進行權衡。應用程序可以根據其對耐用性,可用性和吞吐量的要求來選擇合適的冗餘級別。(譯者注:其實這裏就是CAP理論的權衡)其次,我們要在Kafka中添加一些流處理功能。從Kafka取回消息後,實時應用程序通常會執行類似的操作,例如基於窗口的計數以及將每個消息與輔助存儲中的記錄或另一個流中的消息連接在一起。(譯者注:kafka stream做的事情,其實flink更加完善的支持了)在最低級別上,這可以通過在發佈過程中對聯接密鑰上的消息進行語義分區來支持,以便使用特定鍵發送的所有消息都到達相同的分區(譯者注:其實就是一致性哈希),從而被同一個消費者處理。這爲處理消費類計算機羣集中的分佈式流提供了基礎。最重要的是,我們認爲有用的流實用程序庫(例如不同的窗口功能或聯接技術)將對此類應用程序有益。

參考文獻
[1] http://activemq.apache.org/
[2] http://avro.apache.org/
[3] Cloudera’s Flume, https://github.com/cloudera/flume
[4] http://developer.yahoo.com/blogs/hadoop/posts/2010/06/ena bling_hadoop_batch_processi_1/
[5] Efficient data transfer through zero copy: https://www.ibm.com/developerworks/linux/library/j-zerocopy/
[6] Facebook’s Scribe, http://www.facebook.com/note.php?note_id=32008268919
[7] IBM Websphere MQ: http://www-01.ibm.com/software/integration/wmq/
[8] http://hadoop.apache.org/
[9] http://hadoop.apache.org/hdfs/
[10] http://hadoop.apache.org/zookeeper/
[11] http://www.slideshare.net/cloudera/hw09-hadoop-based-data-mining-platform-for-the-telecom-industry
[12] http://www.slideshare.net/prasadc/hive-percona-2009
[13] https://issues.apache.org/jira/browse/ZOOKEEPER-775
[14] JAVA Message Service: http://download.oracle.com/javaee/1.3/jms/tutorial/1_3_1-fcs/doc/jms_tutorialTOC.html.
[15] Oracle Enterprise Messaging Service: http://www.oracle.com/technetwork/middleware/ias/index-093455.html
[16] http://www.rabbitmq.com/
[17] TIBCO Enterprise Message Service: http://www.tibco.com/products/soa/messaging/
[18] Kafka, http://sna-projects.com/kafka/

點個贊吧~

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