聊聊 Kafka: Kafka 的基礎架構
一、我與快遞小哥的故事
一個很正常的一個工作日,老周正在忙着啪啪啪的敲代碼,辦公司好像安靜的只剩敲代碼的聲音。突然,我的電話鈴聲響起了,頓時打破了這種安靜。
我:喂,哪位?
快遞小哥:我是順豐快遞的,你有個包裹,請問你現在在家嗎?
我:哦,我現在不在家,晚上你再幫我送過來吧。
快遞小哥:要不我幫你放在菜鳥驛站吧?
我:可以可以,謝謝了。
還好有菜鳥驛站,不然工作日加班到很晚纔回家,晚上快遞小哥又下班了,得等到週末我在家快遞小哥才能幫我送了。如果沒有菜鳥驛站的話,我們來看下快遞小哥與我的交互圖:
要是有菜鳥驛站的話,我們再來看下交互圖:
上面故事中的菜鳥驛站就是消息隊列,也就是我們本篇要講的 Kafka;而快遞小哥就是生產者,老周就是消費者。老週一直很忙沒去菜鳥驛站取快遞,就是消息積壓。我給快遞小哥發消息說,確認快遞已經取到了,就是 ACK 機制。
小夥伴們可能發現了菜鳥驛站的好處了,真香。
這裏老周來總結幾點消息隊列的好處也就是使用場景:
-
應用解耦
到了什麼 618、雙 11,快遞小哥要忙瘋了,他得每個電話打,然後詢問客戶在不在家,幾點有時間,這樣完全依賴收貨人,那快遞小哥估計要瘋。有了菜鳥驛站,快遞小哥直接把快遞放在菜鳥驛站,然後通知收貨人過去取就好了,這就讓收貨人與快遞小哥實現瞭解耦。在軟件行業的話,即應用解耦。 -
異步處理
要是沒有菜鳥驛站,快遞小哥得在樓下等你下來拿快遞,拿完了他才能走,這叫同步處理。有了菜鳥驛站,快遞小哥把你的快遞放在了菜鳥驛站,通知你去取,然後他繼續幹別的事情去了,這叫異步處理。異步處理明顯提升了快遞小哥的工作效率,在軟件中,寫異步代碼同樣能提升代碼的執行效率。 -
流量削鋒
雙十一老周買了很多東西,不同的店發的快遞不一樣,有順豐、韻達、中通、申通,都在我週日上午十點這個時間段下去拿,搞得我下樓好頻繁這個時間段,有了菜鳥驛站,我可以喫中午飯再下去順便拿下快遞,這就達到了十點那個時間段的流量削鋒效果。
我與快遞小哥的故事是真實發生的哈,正好我有個讀者前段時間面試順豐的時候被問到 Kafka 了,要我出 Kafka 的內容,所以有了靈感寫了這篇文章。
二、Kafka 介紹
Kafka 是最初由 Linkedin 公司開發,是一個 分佈式、分區的、多副本的、多生產者、多訂閱者,基於 zookeeper 協調的分佈式消息系統。常見可以用於 web/nginx 日誌、訪問日誌,消息服務等。
Linkedin 於 2010 年貢獻給了 Apache 基金會併成爲頂級開源項目。
2.1 基於 zookeeper 協調
這裏老周要提一下,Kafka 2.8.0 版本實現了 Raft 分佈式一致性機制,意味着可以脫離 ZooKeeper 獨立運行了。
ZooKeeper 在 Kafka 中扮演着重要的角色,用來存儲 Kafka 的元數據。ZooKeeper 存儲着 Partition 和 Broker 的元數據 ,同時也負責 Kafka Controller 的選舉工作。
對於 Kafka 來講,ZooKeeper 是一套外部系統,要想部署一套 Kafka 集羣,就要同時部署、管理、監控 ZooKeeper。ZooKeeper 有自己的配置方式、管理工具,和 Kafka 完全不一樣,所以,一起搞兩套分佈式系統,自然就提升了複雜度,也更容易出現問題。有時工作量還會加倍,例如要開啓一些安全特性,Kafka 和 ZooKeeper 中都需要配置。除了複雜度,外部存儲也會降低系統效率。
例如:
-
Kafka 集羣每次啓動的時候,Controller 必須從 ZooKeeper 加載集羣的狀態信息。
-
選舉出一個新的 Controller 之後也會比較麻煩,因爲需要加載元數據,而此時元數據的量可能已經非常大了,這就產生了效率問題。
所以,ZooKeeper 帶來的複雜度、系統效率這兩個問題已經成爲 Kafka 的痛點,Kafka 團隊一直在努力去除對 ZooKeeper 的依賴。Kafka 2.8.0 這個版本終於實現了。
使用 Raft 模式之後,元數據、配置信息都會保存在 @metadata 這個 Topic 中,自動在集羣中複製。這樣 Kafka 就會簡單輕巧很多。
但需要注意的是,Zookeeper-less Kafka 還屬於早期版本,並不完善,所以,現在不要應用在線上產品環境中。
2.2 主要應用場景
-
日誌收集系統
-
消息系統
2.3 Kafka 主要設計目標
-
以時間複雜度爲
O(1)
的方式提供消息持久化能力,即使對TB
級以上數據也能保證常數時間的訪問性能。 -
高吞吐率。即使在非常廉價的商用機器上也能做到單機支持每秒
100K
條消息的傳輸。 -
支持
Kafka Server
間的消息分區,及分佈式消費,同時保證每個partition
傳輸。 -
同時支持離線數據處理和實時數據處理
-
支持在線水平擴展
2.4 兩種主要的消息傳遞模式
2.4.1 點對點模式
點對點模式通常是基於拉取或者輪詢的消息傳送模型,這個模型的特點是發送到隊列的消息被一個且只有一個消費者進行處理。生產者將消息放入消息隊列後,由消費者主動的去拉取消息進行消費。點對點模型的的優點是消費者拉取消息的頻率可以由自己控制。但是消息隊列是否有消息需要消費,在消費者端無法感知,所以在消費者端需要額外的線程去監控。
2.4.2 發佈訂閱模式
發佈訂閱模式是一個基於消息送的消息傳送模型,該模型可以有多種不同的訂閱者。生產者將消息放入消息隊列後,隊列會將消息推送給訂閱過該類消息的消費者。由於是消費者被動接收推送,所以無需感知消息隊列是否有待消費的消息!但是 consumer1、consumer2、consumer3 由於機器性能不一樣,所以處理消息的能力也會不一樣,但消息隊列卻無法感知消費者消費的速度!所以推送的速度成了發佈訂閱模模式的一個問題!假設三個消費者處理速度分別是 8M/s、5M/s、2M/s,如果隊列推送的速度爲 5M/s,則 consumer3 無法承受!如果隊列推送的速度爲 2M/s,則 consumer1、consumer2 會出現資源的極大浪費!
大部分的消息系統選用發佈訂閱模式。Kafka 就是一種發佈訂閱模式。
2.5 Kafka 四個核心 API
-
Producer API
:允許應用程序將記錄流發佈到一個或多個 Kafka 主題。 -
Consumer API
:允許應用程序訂閱一個或多個主題並處理爲其生成的記錄流。 -
Streams API
:允許應用程序充當流處理器,使用一個或多個主題的輸入流,並生成一個或多個輸出主題的輸出流,從而有效地將輸入流轉換爲輸出流。 -
Connector API
:允許構建和運行將 Kafka 主題連接到現有應用程序或數據系統的可重用生產者或使用者。例如,關係數據庫的連接器可能會捕獲對錶的所有更改。
三、Kafka 的優勢
-
高吞吐量
:單機每秒處理幾十上百萬的消息量。即使存儲了許多的消息,它也保持穩定的性能。 -
高性能
:單節點支持上千個客戶端,並保證零停機和零數據丟失。 -
持久化數據存儲
:將消息持久化到磁盤。通過將數據持久化到硬盤以及防止數據丟失。(零拷貝、 順序讀,順序寫、利用的頁緩存) -
分佈式系統,易於向外擴展
。所有的 Producer、Broker 和 Consumer 都會有多個,均爲分佈式的。
無需停機即可擴展機器。多個 Producer、Consumer 可能是不同的應用。 -
可靠性
:Kafka 是分佈式、分區、複製和容錯的。 -
客戶端狀態維護
:消息被處理的狀態是在 Consumer 端維護,而不是由 server 端維護。當失敗時能自動平衡。 -
支持 online 和 offline 的場景
-
支持多種客戶端語言
。Kafka 支持 Java、.NET、PHP、Python 等多種語言。
四、Kafka 的應用場景
4.1 日誌收集
Kafka 可以收集各種服務的 Log,通過 Kafka 以統一接口服務的方式開放給各種 Consumer。
4.2 消息系統
解耦生產者和消費者、緩存消息等。
4.3 用戶活動跟蹤
-
Kafka 經常被用來記錄 Web 用戶或者 App 用戶的各種活動,如瀏覽網頁、搜索、點擊等活動。
-
這些活動信息被各個服務器發佈到 Kafka 的 Topic 中,然後消費者通過訂閱這些 Topic 來做實時的監控分析,亦可保存到數據庫。
4.4 運營指標
Kafka 也經常用來記錄運營監控數據。包括收集各種分佈式應用的數據,生產各種操作的集中反饋,比如報警和報告。
4.5 流式處理
比如 Spark Streaming 和 Storm。
五、基礎架構
5.1 Kafka 架構圖
5.2 消息和批次
-
Kafka 的數據單元稱爲消息。可以把消息看成是數據庫裏的一個 “數據行” 或一條“記錄”。消息由字節數組組成。
-
消息有鍵,鍵也是一個字節數組。當消息以一種可控的方式寫入不同的分區時,會用到鍵。
-
爲了提高效率,消息被分批寫入 Kafka。批次就是一組消息,這些消息屬於同一個主題和分區。
-
把消息分成批次可以減少網絡開銷。批次越大,單位時間內處理的消息就越多,單個消息的傳輸時間就越長。批次數據會被壓縮,這樣可以提升數據的傳輸和存儲能力,但是需要更多的計算處理。
5.3 模式
-
消息模式(schema)有許多可用的選項,以便於理解。如 JSON 和 XML,但是它們缺乏強類型處理能力。Kafka 的許多開發者喜歡使用 Apache Avro。Avro 提供了一種緊湊的序列化格式,模式和消息體分開。當模式發生變化時,不需要重新生成代碼,它還支持強類型和模式進化,其版本既向前兼容,也向後兼容。
-
數據格式的一致性對 Kafka 很重要,因爲它消除了消息讀寫操作之間的耦合性。
5.4 主題和分區
-
Kafka 的消息通過主題進行分類。主題可比是
數據庫的表
或者文件系統裏的文件夾
。主題可以被分爲若干分區,一個主題通過分區分佈於 Kafka 集羣中,提供了橫向擴展的能力。
5.5 生產者和消費者
-
生產者創建消息。消費者消費消息。
-
一個消息被髮布到一個特定的主題上。
-
生產者在默認情況下把消息均衡地分佈到主題的所有分區上:
-
直接指定消息的分區
-
根據消息的 key 散列取模得出分區
-
輪詢指定分區
-
消費者通過偏移量來區分已經讀過的消息,從而消費消息。
-
消費者是消費組的一部分。消費組保證每個分區只能被一個消費者使用,避免重複消費。
5.6 broker 和集羣
-
一個獨立的 Kafka 服務器稱爲 broker。
-
broker 接收來自生產者的消息,爲消息設置偏移量,並提交消息到磁盤保存。
-
broker 爲消費者提供服務,對讀取分區的請求做出響應,返回已經提交到磁盤上的消息。
-
單個 broker 可以輕鬆處理數千個分區以及每秒百萬級的消息量。
每個集羣都有一個 broker 是集羣控制器(自動從集羣的活躍成員中選舉出來)。
控制器負責管理工作:
-
將分區分配給 broker
-
監控 broker
集羣中一個分區屬於一個 broker,該 broker 稱爲分區首領。
一個分區可以分配給多個 broker,此時會發生分區複製。
分區的複製提供了消息冗餘, 高可用 。副本分區不負責處理消息的讀寫。
六、核心概念
6.1 Producer
生產者創建消息。
該角色將消息發佈到 Kafka 的 topic 中。broker 接收到生產者發送的消息後,broker 將該消息追加到當前用於追加數據的 segment 文件中。
一般情況下,一個消息會被髮布到一個特定的主題上。
-
默認情況下通過輪詢把消息均衡地分佈到主題的所有分區上。
-
在某些情況下,生產者會把消息直接寫到指定的分區。這通常是通過消息鍵和分區器來實現的,分區器爲鍵生成一個散列值,並將其映射到指定的分區上。這樣可以保證包含同一個鍵的消息會被寫到同一個分區上。
-
生產者也可以使用自定義的分區器,根據不同的業務規則將消息映射到分區。
6.2 Consumer
消費者讀取消息。
-
消費者訂閱一個或多個主題,並按照消息生成的順序讀取它們。
-
消費者通過檢查消息的偏移量來區分已經讀取過的消息。偏移量是另一種元數據,它是一個不斷遞增的整數值,在創建消息時,Kafka 會把它添加到消息裏。在給定的分區裏,每個消息的偏移量都是唯一的。消費者把每個分區最後讀取的消息偏移量保存在 Zookeeper 或 Kafka 上 ,如果消費者關閉或重啓,它的讀取狀態不會丟失。
-
消費者是消費組的一部分。羣組保證每個分區只能被一個消費者使用。
-
如果一個消費者失效,消費組裏的其他消費者可以接管失效消費者的工作,再平衡,分區重新分配。
6.3 Broker
一個獨立的 Kafka 服務器被稱爲 broker。
broker 爲消費者提供服務,對讀取分區的請求作出響應,返回已經提交到磁盤上的消息。
-
如果某 topic 有 N 個 partition,集羣有 N 個 broker,那麼每個 broker 存儲該 topic 的一個 partition。
-
如果某 topic 有 N 個 partition,集羣有 (N+M) 個 broker,那麼其中有 N 個 broker 存儲該 topic 的一個 partition,剩下的 M 個 broker 不存儲該 topic 的 partition 數據。
-
如果某 topic 有 N 個 partition,集羣中 broker 數目少於 N 個,那麼一個 broker 存儲該 topic 的一個或多個 partition。在實際生產環境中,儘量避免這種情況的發生,這種情況容易導致 Kafka 集羣數據不均衡。
broker 是集羣的組成部分。每個集羣都有一個 broker 同時充當了集羣控制器的角色(自動從集羣的活躍成員中選舉出來)。
控制器負責管理工作,包括將分區分配給 broker 和監控 broker。
在集羣中,一個分區從屬於一個 broker,該 broker 被稱爲分區的首領。
6.4 Topic
每條發佈到 Kafka 集羣的消息都有一個類別,這個類別被稱爲 Topic。
物理上不同 Topic 的消息分開存儲。
主題就好比數據庫的表,尤其是分庫分表之後的邏輯表。
6.5 Partition
-
主題可以被分爲若干個分區,一個分區就是一個提交日誌。
-
消息以追加的方式寫入分區,然後以先入先出的順序讀取。
-
無法在整個主題範圍內保證消息的順序,但可以保證消息在單個分區內的順序。
-
Kafka 通過分區來實現數據冗餘和伸縮性。
-
在需要嚴格保證消息的消費順序的場景下,需要將 partition 數目設爲 1。
6.6 Replicas
Kafka 使用主題來組織數據,每個主題被分爲若干個分區,每個分區有多個副本。那些副本被保存在 broker 上,每個 broker 可以保存成百上千個屬於不同主題和分區的副本。
副本有以下兩種類型:
-
首領副本
每個分區都有一個首領副本。爲了保證一致性,所有生產者請求和消費者請求都會經過這個副本。 -
跟隨者副本
首領以外的副本都是跟隨者副本。跟隨者副本不處理來自客戶端的請求,它們唯一的任務就是從首領那裏複製消息,保持與首領一致的狀態。如果首領發生崩潰,其中的一個跟隨者會被提升爲新首領。
6.7 Offset
6.7.1 生產者 Offset
消息寫入的時候,每一個分區都有一個 offset,這個 offset 就是生產者的 offset,同時也是這個分區的最新最大的 offset。
有些時候沒有指定某一個分區的 offset,這個工作 kafka 幫我們完成。
6.7.2 消費者 Offset
這是某一個分區的 offset 情況,生產者寫入的 offset 是最新最大的值是 12,而當 Consumer A 進行消費時,從 0 開始消費,一直消費到了 9,消費者的 offset 就記錄在 9,Consumer B 就紀錄在了 11。等下一次他們再來消費時,他們可以選擇接着上一次的位置消費,當然也可以選擇從頭消費,或者跳到最近的記錄並從 “現在” 開始消費。
6.8、副本
Kafka 通過副本
保證高可用
。副本分爲首領副本
(Leader) 和跟隨者副本
(Follower)。
跟隨者副本包括同步副本
和不同步副本
,在發生首領副本切換的時候,只有同步副本可以切換爲首領副本。
6.8.1 AR
分區中的所有副本統稱爲AR
(Assigned Repllicas)
AR=ISR+OSR
6.8.2 ISR
所有與leader副本保持一定程度同步的副本(包括Leader)
組成ISR(In-Sync Replicas)
,ISR 集合是 AR 集合中的一個子集。消息會先發送到 leader 副本,然後 follower 副本才能從 leader 副本中拉取消息進行同步,同步期間內 follower 副本相對於 leader 副本而言會有一定程度的滯後。前面所說的 “一定程度” 是指可以忍受的滯後範圍,這個範圍可以通過參數進行配置。
6.8.3 OSR
與leader副本同步滯後過多的副本(不包括leader)副本
,組成OSR(Out-Sync Relipcas)
。在正常情況下,所有的 follower 副本都應該與 leader 副本保持一定程度的同步,即 AR=ISR,OSR 集合爲空。
6.8.4 HW
HW 是High Watermak
的縮寫, 俗稱高水位
,它表示了一個特定消息的偏移量(offset),消費者只能拉取到這個offset之前的消息
。
6.8.5 LEO
LEO 是Log End Offset
的縮寫,它表示了當前日誌文件中下一條待寫入
消息的 offset。
歡迎大家關注我的公衆號【老周聊架構】,Java 後端主流技術棧的原理、源碼分析、架構以及各種互聯網高併發、高性能、高可用的解決方案。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/Y6YYKh39HoDMoZ4vYA9cSw