❝
何為後kafka時代那?kafka自從 2011 年被捐獻給 Apache 基金會到現在,已經發展到現如今的消息隊列事實标準。作為一個優秀的分布式消息系統,Kafka 已經被許多企業采用并成為其大資料架構中不可或缺的一部分。Kafka也 已經不再隻是分布式消息隊列,而是想做內建了分發、存儲和計算的“流式資料平台”。與此同時,大資料“新秀 Pulsar” 站在的kafka的肩膀上,針對kafka使用與維護過程中的痛點問題,明确提到“Pulsar 旨在取代 Apache Kafka 多年的主宰地位”的口号。
❞
kafka作為一個老的基礎元件,很多讀者都已經對其設計和原理十分熟悉,面向Pulsar的沖擊下,很多人或許會猶豫究竟要選擇哪個技術?
本人在 Tencent 中負責維護資料總線與資料內建服務,kafka與pulsar是消息總線中的基本元件需求,并且我們的系統在具體的大資料消息隊列之上,又抽象了一層管道(channel)的概念,使得可以将兩種消息隊列可以可插拔的嵌入服務中。是以,我們團隊同時在使用Kafka與Pulsar。不可否認,Pulsar确實十分優秀,站在了前人的基礎上,在架構底層重新定義的大資料消息隊列。

但是,即使是Pulsar時代下,kafka的存在價值和使用場景是否發生了變化那?
本系列文章會帶大家重新梳理和回顧Kafka的概念、設計、發展曆史和其當下使用的場景。
發展曆程和底層設計
Kafka 是 2010 年左右在 LinkedIn 研發的一套流資料處理平台。LinkedIn發現這些 MQ 系統都有兩個比較通用的缺陷:一是當消費者出現,無法及時消費的時候,資料就會丢掉;二是可延展性問題,MQ 系統很難很好地配合資料的波峰或波谷。
這些需求正好對應當時的消息隊列系統不能解決的一些問題:「橫向拓展、持久化、高吞吐、高性能、甚至是低成本」。是以Kafka這一流處理系統出現後,瞬間成為大資料流處理系統的事實标準。
2010 年LinkedIn開始自己開發 Kafka。設計理念非常簡單,就是一個以 「append-only 日志作為核心的資料存儲結構」。簡單來說,就是我們把資料以日志的方式進行組織,所有對于日志的寫操作,都送出在日志的最末端,對日志也隻能是順序讀取。
從我們當下的角度看待當時的kafka設計确實什麼簡單又十分優雅!kafka能實作上述的:橫向拓展、持久化、高吞吐、高性能都得益于這個基礎設計。
- 「順序讀寫,高吞吐」: HDD 的随機讀取和寫入因為其本身原因會非常慢,但其實如果能夠把所有的讀和寫都按照順序來進行操作,會發現它幾乎可以媲美記憶體的随機通路。kafka利用append-only 日志作為核心的資料存儲結構,隻會對檔案進行順序的讀寫操作,大大的利用了這一優點。【機械硬碟的連續讀寫性能很好,但随機讀寫性能很差,這主要是因為磁頭移動到正确的磁道上需要時間,随機讀寫時,磁頭需要不停的移動,時間都浪費在了磁頭尋址上,是以性能不高。衡量磁盤的重要主要名額是IOPS和吞吐量。】
- 「實體分區,多分區擴充」:在Kafka中,Topic 隻是一個邏輯的概念。每個 Topic 都包含一個或多個 Partition,不同 Partition 可位于不同節點。一方面,由于不同 Partition 可位于不同機器,是以可以充分利用叢集優勢,實作機器間的并行處理。另一方面,由于 Partition 在實體上對應一個檔案夾,即使多個 Partition 位于同一個節點,也可通過配置讓同一節點上的不同 Partition 置于不同的磁盤上,進而實作磁盤間的并行處理,充分發揮多磁盤的優勢。
- 「多副本,保證高可用」:每個broker中的partition都會設定有replication(副本)的個數,生産者寫入的時候首先根據分發政策(有partition按partition,有key按key,都沒有輪詢)寫入到leader中,follower(副本)再跟leader同步資料,這樣有了備份,也可以保證消息資料的不丢失。
- 「低成本」:Kafka 的日志存儲持久化到磁盤,在部署時可以使用成本較低的HDD磁盤。
- 「頁緩存加速」:順序寫檔案時,「讀操作可直接在 Page Cache 内進行。如果消費和生産速度相當,甚至不需要通過實體磁盤(直接通過 Page Cache)交換資料」;頁緩存會将連續的小塊寫組裝成大塊的實體寫進而提高性能;頁緩存會 嘗試将一些寫操作重新按順序排好,進而減少磁盤頭的移動時間;【Cache 層在記憶體中緩存了磁盤上的部分資料。當資料的請求到達時,如果在 Cache 中存在該資料且是最新的,則直接将資料傳遞給使用者程式,免除了對底層磁盤的操作,提高了性能】
- 「零拷貝,充分利用IO」:Kafka 在這裡采用的方案是通過 NIO 的
調用作業系統的 sendfile 實作零拷貝。總共發生 2 次核心資料拷貝、2 次上下文切換和一次系統調用,消除了 CPU 資料拷貝。【零拷貝(Zero-copy)技術指在計算機執行操作時,CPU 不需要先将資料從一個記憶體區域複制到另一個記憶體區域,進而可以減少上下文切換以及 CPU 的拷貝時間。它的作用是在資料報從網絡裝置到使用者程式空間傳遞的過程中,減少資料拷貝次數,減少系統調用,實作 CPU 的零參與,徹底消除 CPU 在這方面的負載】transferTo/transferFrom
系列一:後kafka時代下的消息隊列,Kafka還會走多遠?發展曆程和底層設計Kafka使用痛點後Kafka時代的使用場景版本變更
從我們當下的角度看待當時的kafka設計還是那麼簡潔和優雅,kafka能實作上述的:橫向拓展、持久化、高吞吐、高性能都得益于這個基礎設計。但是,這種簡單的設計也有一些弊端,但是在kafka剛出的那個時候,這個設計和功能确實太優秀了。
Kafka使用痛點
首先先說結論,「後kafka時代下,kafka的某些設計已經比較落後了」。在營運/運維kafka的過程中,其實遇到了很多問題。
而很多問題是在基礎架構确定之後,就決定了會有這樣的結果。
單機器的分區上限問題
雖然 Kafka 的 topic partition 是順序寫入,但是當 broker上有成百上千個topic partition 時,從磁盤角度看就變成了随機寫入,此時磁盤讀寫性能會随着 topic partition 數量的增加而降低,是以 「Kafka broker 上存儲的 topic partition 數量是有限制的」。
這大大限制了kafka在多主題情況下的使用。
非存儲與計算分離的架構
kafka并不是一個存儲與計算分離的架構,是以無法從存儲和計算單個次元進行擴容。
資料存儲和消息隊列服務綁定,叢集擴縮容/分區均衡需要大量拷貝資料,造成叢集性能下降,并且帶來了很大的運維成本。
一個分區隻能歸屬于一台機器帶來的檔案存儲
Kafka中根據設定的保留期來删除消息。有可能消息沒被消費,過期後被删除。 不支援TTL。
但是這其中的本質問題來自于:一個分區隻能歸屬于一台Broker機器,如果想要擴容的話,隻能擴分區,拆分區
在極端情況下,如果原有kafka叢集負載到達50%,流量這時如果翻三四倍,這對kafka的運維來說簡直是個災難!
運維成本高
如果某個機器磁盤滿了,需要顯式的使用工具遷移分區(kafka-reassign-partitions.sh)
資料存儲和消息隊列服務綁定,叢集擴縮容/分區均衡需要大量拷貝資料,會帶來了很大的運維成本。
随着 Kafka 叢集規模的增長,Kakfa 叢集的運維成本急劇增長,需要投入大量的人力進行日常運維。在某網際網路公司中,擴容一台機器到 Kafka 叢集并進行分區均衡,需要 0.5人/天;縮容一台機器需要 1 人/天。
PageCache 污染問題,造成讀寫性能下降
Kafka對page cache需求巨大。根據經驗值,為Kafka配置設定6~8GB的堆記憶體就已經足足夠用了,将剩下的系統記憶體都作為page cache空間,可以最大化I/O效率。
另一個需要特别注意的問題是lagging consumer,即那些消費速率慢、明顯落後的consumer。它們要讀取的資料有較大機率不在broker page cache中,是以會增加很多不必要的讀盤操作。
比這更壞的是,「lagging consumer讀取的“冷”資料仍然會進入page cache,污染了多數正常consumer要讀取的“熱”資料」,連帶着正常consumer的性能變差。在生産環境中,這個問題尤為重要。
在 catch-up 讀場景下,容易出現 PageCache 污染,造成讀寫性能下降。雖然kafka可以利用PageCache進行讀取加速,在一些場景下實踐效果不佳。
「Kafka不支援讀寫分離」
在 Kafka 中,生産者寫入消息、消費者讀取消息的操作都是與 leader 副本進行互動的,從 而實作的是一種「主寫主讀」的生産消費模型。 Kafka 并不支援「主寫從讀」。
其實kafka的「主寫主讀」也是有一些優點的:
- 可以簡化代碼的實作邏輯,減少出錯的可能;
- 将負載粒度細化均攤,與主寫從讀相比,不僅負載效能更好,而且對使用者可控;
- 沒有延時的影響;
- 在副本穩定的情況下,不會出現資料不一緻的情況。
但是這些也不能算是完全的優點,「隻是在目前kafka架構下,做到讀寫分離的收益不如主寫主讀方案。」
kafka中IO 不隔離,是以「消費者在清除 Backlog 時會影響其他生産者和消費者」。
後Kafka時代的使用場景
什麼樣的場景下可以繼續用kafka?
在叢集内topic不多或增長速度不是特别快的情況下,kafka依舊是很好的選擇。
不需要複雜的企業級場景的時候,kafka仍舊是首選。
Kafka 原生的叢集模式使用簡單,能滿足少量業務的需要。但對于大型企業(網站),大量的業務使用 Kafka,資料量、流量極大,必然同時存在多個叢集,需要對使用者的接入、運作時監控、叢集運維提供統一的解決方案
版本變更
kafka 0.7 版本
kafka開源後的第一個正式的版本
提供了資料的壓縮以及 MirrorMaker,也就是跨叢集之間的資料拷貝。
這個版本太過久遠,我們就不再占用篇幅介紹了
kafka 0.8 版本
Kafka 0.8.0 裡面加入了多副本功能,也就是基于備份的一個高可用性的特性。
該版本中提出了一個叫做 ISR,或者叫做實時備份清單的機制。我們把所有的備份分為已同步和未同步的備份。
已同步的備份指,Leader 所有的 Data 在 replica 裡面都有;未同步的很簡單,由于可能比較慢,或者備份還不完整,也許有些資料在 Leader 上面有,但是在 replica 上沒有。Leader 可以通過來管理這樣的一個清單來做到實時的修改。同時,釋出者釋出資訊的時候,可以要求備份方式。
kafka 0.9 版本
當 Kafka 叢集不斷變大、使用場景不斷增多的時候,多租戶之間的影響就會非常顯著,一個人可以影響其他所有使用者。
有一個員工寫的用戶端,當擷取中繼資料失敗時會一直發請求,并部署到了幾十台機器上,結果就影響了所有的其他使用者。是以我們在 0.9 裡第一個要加的重大機制就是配額,限定每一個 user 能夠用多大的流量跟 Kafka 互動。如果你超過配額,Kafka broker 就故意延遲你的請求,使一個 User 不會影響别人。這就是 0.9 的配額機制。
之前版本,Kafka其實存在一個比較大的隐患,就是利用 Zookeeper 來存儲記錄每個消費者/組的消費進度。雖然,在使用過程當中,JVM幫助我們完成了一些優化,但是消費者需要頻繁的去與 Zookeeper 進行互動,而利用ZKClient的API操作Zookeeper頻繁的Write其本身就是一個比較低效的Action,對于後期水準擴充也是一個比較頭疼的問題。如果期間 Zookeeper 叢集發生變化,那 Kafka 叢集的吞吐量也跟着受影響。
從kafka-0.9版本及以後,kafka的消費者組和offset資訊就不存zookeeper了,而是存到broker伺服器上,是以,如果你為某個消費者指定了一個消費者組名稱(group.id),那麼,一旦這個消費者啟動,這個消費者組名和它要消費的那個topic的offset資訊就會被記錄在broker伺服器上。
kafka 0.10 版本
Kafka Streams 是在 0.10 裡面加入的,它是一個流處理的平台,或者叫它是流處理的一個庫。它是基于釋出端和消費端的處理平台,它能夠做到的是 Event-at-a-time、Stateful,并且支援像 Windowing 這樣的操作,支援 Highly scalable、distributed、fault tolerant。所有這些都很大程度上利用了 Kafka broker,也就是伺服器端本身的延展性和高可用性。
簡單來說,Kafka Streams 所做的就是從 Kafka 的 Topic 裡實時地抓取資料。這個資料會通過使用者所寫拓撲結構,把所有的 record 實時進行 transform 之後,最終再寫回到 Kafka 裡面,是個很簡單的流資料處理。那麼它怎麼做延展性呢?也很簡單,當使用者寫好一個拓撲結構以後,可以在多個機器,或者多個容器、多個虛拟機、甚至是多個 CPU 上面,部署多個應用,當應用同時進行的時候,會利用 Kafka 自動地劃分每一個不同的應用所抓取的不同 partition 的資料
kafka 1.0版本
在該版本下做到 Exactly-Once,這才能使 Kafka 作為一個成熟的流資料平台
非 Exactly-Once 是指由于網絡延遲或其他各種原因,導緻消息重複發送甚至重複處理。那麼直白來說 Exactly-Once 的定義,就是從應用的角度來說,當發生了錯誤,希望做到每一個接收到的 record,處理結果會被反映到它的處理狀态中,一次且僅有一次,也就是 Exactly-Once。
之後的版本中,雖然在持續疊代,但是從kafka的底層架構來看,沒有其他的重大更新了
參考資料:
https://mp.weixin.qq.com/s/p-jJVMjh9yr8QVqgRKtPtA
https://zhuanlan.zhihu.com/p/337861077
https://zhuanlan.zhihu.com/p/344277683
歡迎關注公衆号:大資料技術事務所

此号就持續更新:
大資料面試知識、大資料學習路線、大資料知識圖譜
号主在T家大廠中做大資料相關工作,此号後續會持續更新 kafka/pulsar/資料湖/spark/flink/presto等相關知識。
周更系列,感謝支援