推ter 的 Kafka 遷移歷程
推ter 的實時性特點為 推ter 的工程團隊帶來了獨特而具有挑戰性的問題。我們需要快速發布突發新聞,向用戶提供相關廣告,并解決很多其他實時性問題。推ter 的 Pub/Sub 系統為 推ter 團隊提供了處理這些工作負載的基礎設施。
推ter 的 Messaging 團隊過去幾年一直在運行一個內部 Pub/Sub 系統,叫作 EventBus(建立在 Apache DistributedLog 之上),但我們最近決定轉向 Apache Kafka,不僅針對已有的用例,還包括新增的用例。在這篇文章中,我們將介紹為什么我們選擇采用 Kafka 作為 推ter 的 Pub/Sub 系統,以及我們在遷移過程中遇到的各種挑戰。
Kafka 是什么?
Apache Kafka 是一個開源的分布式流式處理平臺,可以高吞吐和低延遲地傳輸數據。Kafka 最初是在 LinkedIn 誕生,并于 2011 年開源,從那時起開始被社區廣泛采用,包括很多其他公司在內,使其成為業界首選的事實上的實時消息系統。
Kafka 的核心就是一個基于分布式提交日志構建的 Pub/Sub 系統,提供了很多非常好的特性,例如水平可伸縮性和容錯性。從那以后,Kafka 已經從消息系統發展成為一個成熟的流式處理平臺。
為什么要遷移?
你可能想知道為什么 推ter 需要自己構建內部的消息傳遞系統。幾年前,推ter 也曾經使用過 Kafka(0.7 版本),但我們發現在某些方面它無法滿足我們的要求——主要是在讀取期間進行的 I/O 操作數量,而且缺乏持久化特性和復制機制。然而,隨著時間推移,硬件和 Kafka 都已經走過了漫長的道路,這些問題現在都已經得到了解決。硬件的改進已經使 SSD 的價格足夠便宜,這解決了我們之前在 HDD 上看到的隨機讀取的 I/O 問題,而且服務器 NIC 具有更多的帶寬,就沒有那么必要分離服務和存儲層(EventBus 會這么做)。此外,較新版本的 Kafka 現在支持數據復制,提供了我們想要的持久性保證。
將 推ter 的所有 Pub/Sub 用例遷移到一個全新的系統將是一個非常耗費成本的過程。所以,遷移到 Kafka 的決定絕對不是自然發生的,而是經過精心策劃的,并且是由數據驅動的。遷移到 Kafka 的動機可歸納為兩個方面:成本和社區。
成本
在向整個公司宣布遷移到 Kafka 之前,我們的團隊花了幾個月時間評估了 Kafka 在與 EventBus 類似的工作負載下的表現——持久寫入、尾部讀取、追趕讀取和高扇出讀取,以及一些灰色故障情況(例如減慢集群中的特定 Kafka 代理)。
在性能方面,我們看到 Kafka 的延遲顯著降低,無論是根據消息創建時間戳來衡量吞吐量,還是根據消費者讀取消息時間戳來衡量吞吐量。

不同吞吐量下 EventBus 和 Kafka 之間的 P99 延遲比較
這可以歸因于幾個因素,可能包括但不限于:
- 在 EventBus 中,服務層和存儲層是分離的,這引入了額外的跳轉(網絡時間和通過 JVM 代理層的時間),而在 Kafka 中只有一個進程處理存儲和請求服務(參見下圖)。
- EventBus 在通過 fsync() 調用進行寫入時會阻塞,而 Kafka 在后臺依賴操作系統進行 fsync()。
- Kafka 使用零拷貝。

EventBus(左)和 Kafka(右)之間的架構比較
從成本的角度來看,EventBus 需要服務層(針對高網絡吞吐量進行了優化)和存儲層(針對磁盤進行了優化)的硬件,而 Kafka 使用單臺主機就可以提供這兩者。因此,EventBus 需要更多的機器來提供與 Kafka 相同的工作負載。對于單個消費者,我們節省了 68%的資源,對于擁有多個消費者的案例,我們節省了 75%的資源。但有一個問題是,對于嚴重依賴帶寬的工作負載(非常高的扇出讀取),EventBus 理論上可能更有效,因為我們可以獨立地擴展服務層。但是,我們在實踐中發現,我們的扇出沒有那么極端,分離服務層是不值得的。
社區
如上所述,Kafka 已經得到了廣泛采用。我們可以利用數百名開發人員為 Kafka 項目所做出的貢獻,他們修復錯誤、改進和添加新功能,這比 EventBus/DistributedLog 的八名工程師所做的要好得多。此外,推ter 內部用戶在 EventBus 中想要的很多功能已經在 Kafka 中提供了,例如流式處理庫、至少一次 HDFS 管道,以及恰好一次性處理。
此外,當我們遇到客戶端或服務器問題時,我們可以通過搜索網絡輕松快速地找到解決方案,因為很可能其他人之前也遇到了同樣的問題。另外,相比不太受歡迎的項目,受歡迎的項目的文檔通常更加詳盡。
采用 Kafka 等熱門項目,并向這些項目回饋我們的貢獻,這樣做的另一個重要原因是為了招聘。一方面,通過向 Kafka 社區回饋貢獻,可以讓人們了解 推ter 的工程。另一方面,由于新工程師已經熟悉了這些技術,因此為團隊招聘工程師要容易得多。
挑戰
盡管遷移到 Kafka 看起來非常棒,但過程并不是一帆風順的。我們在這個過程中遇到了很多技術挑戰和適應性挑戰。
從技術角度來看,我們遇到的一些挑戰包括配置調優和 Kafka Streams 庫。與很多分布式系統一樣,為了支持 推ter 的實時性用例,需要對大量配置進行微調。在運行 Kafka Streams 時,我們發現 Kafka Streams 庫中的元數據大小存在一些問題,這些問題是由于老版本的客戶端在關閉后仍然保留元數據造成的。
另一方面,Kafka 與 EventBus 存在架構差異,我們不得不以不同的方式配置系統和調試問題。例如,如何在 EventBus(仲裁寫入)和 Kafka(主從復制)中完成復制。寫請求在 EventBus 中并行發送,而 Kafka 要求從節點僅在主節點收到寫請求后才復制寫請求。此外,兩個系統之間的持久性模型是非常不同的—— EventBus 僅在數據持久化到磁盤時確認寫入成功,而 Kafka 復制本身就具有持久性保證,并在將數據持久存儲在磁盤上之前確認寫入請求。我們不得不重新考慮我們對數據持久性的定義:數據的所有三個副本同時失敗是不太可能的,因此沒有必要在每個副本中同步數據來提供我們想要的持久性保證。
前行
在接下來的幾個月里,我們計劃將我們的用戶從 EventBus 遷移到 Kafka,這將有助于降低運營 推ter Pub/Sub 系統的成本,并使我們的用戶能夠使用 Kafka 提供的其他功能。我們將持續關注生態系統中的不同消息傳遞和流式處理系統,并確保我們的團隊為我們的用戶和 推ter 做出正確的決策,即使這些決策很艱難。
英文原文:
https://blog.推ter.com/engineering/en_us/topics/insights/2018/推ters-kafka-adoption-story.html