- 深入理解Kafka:核心設計與實踐原理
- 朱忠華
- 2721字
- 2019-07-09 14:50:28
1.1 基本概念
一個典型的 Kafka 體系架構包括若干 Producer、若干 Broker、若干 Consumer,以及一個ZooKeeper集群,如圖1-1所示。其中ZooKeeper是Kafka用來負責集群元數(shù)據(jù)的管理、控制器的選舉等操作的。Producer將消息發(fā)送到Broker,Broker負責將收到的消息存儲到磁盤中,而Consumer負責從Broker訂閱并消費消息。

圖1-1 Kafka體系結構
整個Kafka體系結構中引入了以下3個術語。
(1)Producer:生產者,也就是發(fā)送消息的一方。生產者負責創(chuàng)建消息,然后將其投遞到Kafka中。
(2)Consumer:消費者,也就是接收消息的一方。消費者連接到Kafka上并接收消息,進而進行相應的業(yè)務邏輯處理。
(3)Broker:服務代理節(jié)點。對于Kafka而言,Broker可以簡單地看作一個獨立的Kafka服務節(jié)點或Kafka服務實例。大多數(shù)情況下也可以將Broker看作一臺Kafka服務器,前提是這臺服務器上只部署了一個Kafka實例。一個或多個Broker組成了一個Kafka集群。一般而言,我們更習慣使用首字母小寫的broker來表示服務代理節(jié)點。
在Kafka中還有兩個特別重要的概念—主題(Topic)與分區(qū)(Partition)。Kafka中的消息以主題為單位進行歸類,生產者負責將消息發(fā)送到特定的主題(發(fā)送到Kafka集群中的每一條消息都要指定一個主題),而消費者負責訂閱主題并進行消費。
主題是一個邏輯上的概念,它還可以細分為多個分區(qū),一個分區(qū)只屬于單個主題,很多時候也會把分區(qū)稱為主題分區(qū)(Topic-Partition)。同一主題下的不同分區(qū)包含的消息是不同的,分區(qū)在存儲層面可以看作一個可追加的日志(Log)文件,消息在被追加到分區(qū)日志文件的時候都會分配一個特定的偏移量(offset)。offset是消息在分區(qū)中的唯一標識,Kafka通過它來保證消息在分區(qū)內的順序性,不過offset并不跨越分區(qū),也就是說,Kafka保證的是分區(qū)有序而不是主題有序。
如圖 1-2 所示,主題中有 4 個分區(qū),消息被順序追加到每個分區(qū)日志文件的尾部。Kafka中的分區(qū)可以分布在不同的服務器(broker)上,也就是說,一個主題可以橫跨多個broker,以此來提供比單個broker更強大的性能。

圖1-2 消息追加寫入
每一條消息被發(fā)送到broker之前,會根據(jù)分區(qū)規(guī)則選擇存儲到哪個具體的分區(qū)。如果分區(qū)規(guī)則設定得合理,所有的消息都可以均勻地分配到不同的分區(qū)中。如果一個主題只對應一個文件,那么這個文件所在的機器 I/O 將會成為這個主題的性能瓶頸,而分區(qū)解決了這個問題。在創(chuàng)建主題的時候可以通過指定的參數(shù)來設置分區(qū)的個數(shù),當然也可以在主題創(chuàng)建完成之后去修改分區(qū)的數(shù)量,通過增加分區(qū)的數(shù)量可以實現(xiàn)水平擴展。
Kafka 為分區(qū)引入了多副本(Replica)機制,通過增加副本數(shù)量可以提升容災能力。同一分區(qū)的不同副本中保存的是相同的消息(在同一時刻,副本之間并非完全一樣),副本之間是“一主多從”的關系,其中l(wèi)eader副本負責處理讀寫請求,follower副本只負責與leader副本的消息同步。副本處于不同的broker中,當leader副本出現(xiàn)故障時,從follower副本中重新選舉新的leader副本對外提供服務。Kafka通過多副本機制實現(xiàn)了故障的自動轉移,當Kafka集群中某個broker失效時仍然能保證服務可用。
如圖1-3所示,Kafka集群中有4個broker,某個主題中有3個分區(qū),且副本因子(即副本個數(shù))也為3,如此每個分區(qū)便有1個leader副本和2個follower副本。生產者和消費者只與leader副本進行交互,而follower副本只負責消息的同步,很多時候follower副本中的消息相對leader副本而言會有一定的滯后。

圖1-3 多副本架構
Kafka 消費端也具備一定的容災能力。Consumer 使用拉(Pull)模式從服務端拉取消息,并且保存消費的具體位置,當消費者宕機后恢復上線時可以根據(jù)之前保存的消費位置重新拉取需要的消息進行消費,這樣就不會造成消息丟失。
分區(qū)中的所有副本統(tǒng)稱為AR(Assigned Replicas)。所有與leader副本保持一定程度同步的副本(包括leader副本在內)組成ISR(In-Sync Replicas),ISR集合是AR集合中的一個子集。消息會先發(fā)送到leader副本,然后follower副本才能從leader副本中拉取消息進行同步,同步期間內follower副本相對于leader副本而言會有一定程度的滯后。前面所說的“一定程度的同步”是指可忍受的滯后范圍,這個范圍可以通過參數(shù)進行配置。與leader副本同步滯后過多的副本(不包括leader副本)組成OSR(Out-of-Sync Replicas),由此可見,AR=ISR+OSR。在正常情況下,所有的 follower 副本都應該與 leader 副本保持一定程度的同步,即 AR=ISR,OSR集合為空。
leader副本負責維護和跟蹤ISR集合中所有follower副本的滯后狀態(tài),當follower副本落后太多或失效時,leader副本會把它從ISR集合中剔除。如果OSR集合中有follower副本“追上”了leader副本,那么leader副本會把它從OSR集合轉移至ISR集合。默認情況下,當leader副本發(fā)生故障時,只有在ISR集合中的副本才有資格被選舉為新的leader,而在OSR集合中的副本則沒有任何機會(不過這個原則也可以通過修改相應的參數(shù)配置來改變)。
ISR與HW和LEO也有緊密的關系。HW是High Watermark的縮寫,俗稱高水位,它標識了一個特定的消息偏移量(offset),消費者只能拉取到這個offset之前的消息。
如圖 1-4 所示,它代表一個日志文件,這個日志文件中有 9 條消息,第一條消息的 offset(LogStartOffset)為0,最后一條消息的offset為8,offset為9的消息用虛線框表示,代表下一條待寫入的消息。日志文件的HW為6,表示消費者只能拉取到offset在0至5之間的消息,而offset為6的消息對消費者而言是不可見的。

圖1-4 分區(qū)中各種偏移量的說明
LEO是Log End Offset的縮寫,它標識當前日志文件中下一條待寫入消息的offset,圖1-4中offset為9的位置即為當前日志文件的LEO,LEO的大小相當于當前日志分區(qū)中最后一條消息的offset值加1。分區(qū)ISR集合中的每個副本都會維護自身的LEO,而ISR集合中最小的LEO即為分區(qū)的HW,對消費者而言只能消費HW之前的消息。
注意要點:很多資料中誤將圖1-4中的offset為5的位置看作HW,而把offset為8的位置看作LEO,這顯然是不對的。
為了讓讀者更好地理解ISR集合,以及HW和LEO之間的關系,下面通過一個簡單的示例來進行相關的說明。如圖1-5所示,假設某個分區(qū)的ISR集合中有3個副本,即一個leader副本和2個follower副本,此時分區(qū)的LEO和HW都為3。消息3和消息4從生產者發(fā)出之后會被先存入leader副本,如圖1-6所示。

圖1-5 寫入消息(情形1)

圖1-6 寫入消息(情形2)
在消息寫入leader副本之后,follower副本會發(fā)送拉取請求來拉取消息3和消息4以進行消息同步。
在同步過程中,不同的 follower 副本的同步效率也不盡相同。如圖 1-7 所示,在某一時刻follower1完全跟上了leader副本而follower2只同步了消息3,如此leader副本的LEO為5,follower1的LEO為5,follower2的LEO為4,那么當前分區(qū)的HW取最小值4,此時消費者可以消費到offset為0至3之間的消息。

圖1-7 寫入消息(情形3)
寫入消息(情形4)如圖1-8所示,所有的副本都成功寫入了消息3和消息4,整個分區(qū)的HW和LEO都變?yōu)?,因此消費者可以消費到offset為4的消息了。

圖1-8 寫入消息(情形4)
由此可見,Kafka 的復制機制既不是完全的同步復制,也不是單純的異步復制。事實上,同步復制要求所有能工作的 follower 副本都復制完,這條消息才會被確認為已成功提交,這種復制方式極大地影響了性能。而在異步復制方式下,follower副本異步地從leader副本中復制數(shù)據(jù),數(shù)據(jù)只要被leader副本寫入就被認為已經成功提交。在這種情況下,如果follower副本都還沒有復制完而落后于leader副本,突然leader副本宕機,則會造成數(shù)據(jù)丟失。Kafka使用的這種ISR的方式則有效地權衡了數(shù)據(jù)可靠性和性能之間的關系。
- Arduino by Example
- Mastering QGIS
- 深入實踐Spring Boot
- 編程珠璣(續(xù))
- UI智能化與前端智能化:工程技術、實現(xiàn)方法與編程思想
- 教孩子學編程:C++入門圖解
- R語言與網絡輿情處理
- jQuery炫酷應用實例集錦
- HTML5 APP開發(fā)從入門到精通(微課精編版)
- Data Science Algorithms in a Week
- 愛上C語言:C KISS
- Learning Concurrency in Python
- 高性能PHP 7
- Python全棧開發(fā):數(shù)據(jù)分析
- Web前端開發(fā)精品課:HTML5 Canvas開發(fā)詳解