【大數據嗶嗶集20210123】別問,問就是Kafka最可靠

高可靠性分析

Kafka的高可靠性的保障來源於其健壯的副本(replication)策略。通過調節其副本相關參數,可以使得Kafka在性能和可靠性之間運轉的遊刃有餘。Kafka從0.8.x版本開始提供Partition級別的複製,replication數量可以配置文件(default.replication.refactor)中或者創建Topic的時候指定。

這裏先從Kafka文件存儲機制入手,從最底層瞭解Kafka的存儲細節,進而對存儲有個微觀的認知。之後通過Kafka複製原理和同步方式來闡述宏觀層面的概念。最後從ISR,HW,leader選舉以及數據可靠性和持久性保證等等各個維度來豐富對Kafka相關知識點的認知。

Kafka文件存儲機制

Kafka中消息是以Topic進行分類的,生產者通過Topic向Kafka Broker發送消息,消費者通過Topic讀取數據。然而Topic在物理層面又能以Partition爲分組,一個Topic可以分成若干個Partition,那麼Topic以及Partition又是怎麼存儲的呢?Partition還可以細分爲Segment,一個partition物理上由多個Segment組成,那麼這些Segment又是什麼呢?下面我們來一一揭曉。

爲了便於說明問題,假設這裏只有一個Kafka集羣,且這個集羣只有一個Kafka Broker,即只有一臺物理機。在這個Kafka Broker中配置log.dirs=/tmp/Kafka-logs,以此來設置Kafka消息文件存儲目錄,與此同時創建一個名爲topic_zzh_test的Topic,Partition的數量爲4(kafka-topics.sh --create --zookeeper localhost:2181 --partitions 4 --topic topic_zzh_test --replication-factor 1)。那麼我們此時在/tmp/Kafka-logs目錄中可以看到生成了4個目錄:

drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_vms_test-0
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_vms_test-1
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_vms_test-2
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_vms_test-3

在Kafka文件存儲中,同一個Topic下有多個不同的Partition,每個Partiton爲一個目錄,Partition的名稱規則爲:topic名稱+有序序號,第一個序號從0開始計,最大的序號爲Partition數量減1,Partition是實際物理上的概念,而Topic是邏輯上的概念。

上面提到Partition還可以細分爲Segment,這個Segment又是什麼?如果就以Partition爲最小存儲單位,我們可以想象當Kafka Producer不斷髮送消息,必然會引起Partition文件的無限擴張,這樣對於消息文件的維護以及已經被消費的消息的清理帶來嚴重的影響,所以這裏以Segment爲單位又將Partition細分。每個Partition(目錄)相當於一個巨型文件被平均分配到多個大小相等的Segment(段)數據文件中(每個Segment文件中消息數量不一定相等)這種特性也方便Old Segment的刪除,即方便已被消費的消息的清理,提高磁盤的利用率。每個Partition只需要支持順序讀寫就行,Segment的文件生命週期由服務端配置參數(log.segment.bytes,log.roll.{ms,hours}等若干參數)決定。

Segment文件由兩部分組成,分別爲index文件和log文件,分別表示爲Segment索引文件和數據文件。這兩個文件的命令規則爲:Partition全局的第一個segment從0開始,後續每個Segment文件名爲上一個Segment文件最後一條消息的offset值,數值大小爲64位,20位數字字符長度,沒有數字用0填充,如下:

00000000000000000000.index
00000000000000000000.log
00000000000000170410.index
00000000000000170410.log
00000000000000239430.index
00000000000000239430.log

以上面的Segment文件爲例,展示出Segment:00000000000000170410的index文件和log文件的對應的關係,如下圖:

file

如上圖,index索引文件存儲大量的元數據,log數據文件存儲大量的消息,索引文件中的元數據指向對應數據文件中message的物理偏移地址。

1. 如何根據索引文件元數據定位數據位置?
如:index索引文件元數據[3,348],在log數據文件中表示第3個消息,在全局partition中表示170410+3=170413個消息,該條消息在相應log文件中的物理偏移地址爲348。
2. 那麼如何從partition中通過offset查找message呢?
如:讀取offset=170418的消息,查找segment文件,其中,
α. 00000000000000000000.index爲最開始的文件,
β. 00000000000000170410.index(start offset=170410+1=170411),
γ. 00000000000000239430.index(start offset=239430+1=239431),
因此,定位offset=170418在00000000000000170410.index索引文件中。其他後續文件可以依次類推,以偏移量命名並排列這些文件,然後根據二分查找法就可以快速定位到具體文件位置。其次,根據00000000000000170410.index文件中的[8,1325]定位到00000000000000170410.log文件中的1325的位置進行讀取。
3. 那麼怎麼知道何時讀完本條消息,否則就讀到下一條消息的內容了?
因爲消息都具有固定的物理結構,包括:offset(8 Bytes)、消息體的大小(4 Bytes)、crc32(4 Bytes)、magic(1 Byte)、attributes(1 Byte)、key length(4 Bytes)、key(K Bytes)、payload(N Bytes)等等字段,可以確定一條消息的大小,即讀取到哪裏截止。

複製原理和同步方式

Kafka中Topic的每個Partition有一個預寫式的日誌文件,雖然Partition可以繼續細分爲若干個Segment文件,但是對於上層應用來說可以將Partition看成最小的存儲單元(一個有多個Segment文件拼接的“巨型”文件),每個Partition都由一些列有序的、不可變的消息組成,這些消息被連續的追加到Partition中。

file

上圖中有兩個新名詞:HW和LEO。這裏先介紹下LEO,LogEndOffset的縮寫,表示每個Partition的log最後一條Message的位置。HW是HighWatermark的縮寫,是指Consumer能夠看到的此Partition的位置,這個涉及到多副本的概念,這裏先提及一下,下節再詳表。

言歸正傳,爲了提高消息的可靠性,Kafka每個Topic的Partition有N個副本(replicas),其中N(>=1)是Topic的複製因子(replica fator)的個數。Kafka通過多副本機制實現故障自動轉移,當Kafka集羣中一個Broker失效情況下仍然保證服務可用。在Kafka中發生複製時確保Partition的日誌能有序地寫到其他節點上,N個replicas中,其中一個replica爲Leader,其他都爲Follower, Leader處理Partition的所有寫請求,與此同時,Follower會被動定期地去複製Leader上的數據。

如下圖所示,Kafka集羣中有4個Broker, 某Topic有3個Partition,且複製因子即副本個數也爲3:

file

Kafka提供了數據複製算法保證,如果Leader發生故障或掛掉,將選舉一個新Leader,並接受客戶端消息的寫入。Kafka確保從同步副本列表中選舉一個副本爲Leader,或者說Follower追趕Leader數據。Leader負責維護和跟蹤ISR(In-Sync Replicas的縮寫,即副本同步隊列)中所有Follower滯後的狀態。當Producer發送一條消息到Broker後,Leader寫入消息並複製到所有Follower。消息提交之後才被成功複製到所有的同步副本。消息複製延遲受最慢的Follower限制,重要的是快速檢測慢副本,如果Follower“落後”太多或者失效,leader將會把它從ISR中刪除。

ISR

上節我們涉及到ISR (In-Sync Replicas),這個是指副本同步隊列。副本數對Kafka的吞吐率是有一定的影響,但極大的增強了可用性。默認情況下,Kafka的replica數量爲1,即每個Partition都有一個唯一的Leader,爲了確保消息的可靠性,通常應用中將其值(由Broker的參數default.replication.factor指定)大小設置爲大於1,比如3。 所有的副本(replicas)統稱爲Assigned Replicas,即AR。ISR是AR中的一個子集,由Leader維護ISR列表,Follower從Leader同步數據有一些延遲(包括延遲時間replica.lag.time.max.ms和延遲條數replica.lag.max.messages兩個維度, 當前最新的版本0.10.x中只支持replica.lag.time.max.ms這個維度),任意一個超過閾值都會把Follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的Follower也會先存放在OSR中。AR=ISR+OSR。

Kafka 0.9.0.0版本後移除了replica.lag.max.messages參數,只保留了replica.lag.time.max.ms作爲ISR中副本管理的參數。爲什麼這樣做呢?replica.lag.max.messages表示當前某個副本落後Leader的消息數量超過了這個參數的值,那麼Leader就會把Follower從ISR中刪除。假設設置replica.lag.max.messages=4,那麼如果Producer一次傳送至Broker的消息數量都小於4條時,因爲在leader接受到Producer發送的消息之後,而follower副本開始拉取這些消息之前,follower落後leader的消息數不會超過4條消息,故此沒有follower移出ISR,所以這時候replica.lag.max.message的設置似乎是合理的。但是Producer發起瞬時高峯流量,Producer一次發送的消息超過4條時,也就是超過replica.lag.max.messages,此時Follower都會被認爲是與Feader副本不同步了,從而被踢出了ISR。但實際上這些Follower都是存活狀態的且沒有性能問題。那麼在之後追上Leader,並被重新加入了ISR。於是就會出現它們不斷地剔出ISR然後重新迴歸ISR,這無疑增加了無謂的性能損耗。而且這個參數是Broker全局的。設置太大了,影響真正“落後”Follower的移除;設置的太小了,導致Follower的頻繁進出。無法給定一個合適的replica.lag.max.messages的值,故此,新版本的Kafka移除了這個參數。

注意:ISR中包括:Leader和Follower。

上面一節還涉及到一個概念,即HW。HW俗稱高水位,HighWatermark的縮寫,取一個Partition對應的ISR中最小的LEO作爲HW,Consumer最多隻能消費到HW所在的位置。另外每個replica都有HW,Leader和Follower各自負責更新自己的HW的狀態。對於Leader新寫入的消息,Consumer不能立刻消費,Leader會等待該消息被所有ISR中的replicas同步後更新HW,此時消息才能被Consumer消費。這樣就保證瞭如果Leader所在的Broker失效,該消息仍然可以從新選舉的Leader中獲取。對於來自內部Broker的讀取請求,沒有HW的限制。

下圖詳細的說明了當Producer生產消息至Broker後,ISR以及HW和LEO的流轉過程:

file

由此可見,Kafka的複製機制既不是完全的同步複製,也不是單純的異步複製。事實上,同步複製要求所有能工作的Follower都複製完,這條消息纔會被commit,這種複製方式極大的影響了吞吐率。而異步複製方式下,Follower異步的從Leader複製數據,數據只要被Leader寫入log就被認爲已經commit,這種情況下如果follower都還沒有複製完,落後於Leader時,突然Leader宕機,則會丟失數據。而Kafka的這種使用ISR的方式則很好的均衡了確保數據不丟失以及吞吐率。

Kafka的ISR的管理最終都會反饋到Zookeeper節點上。具體位置爲:/brokers/topics/[topic]/partitions/[partition]/state。目前有兩個地方會對這個Zookeeper的節點進行維護:

  1. Controller來維護:Kafka集羣中的其中一個Broker會被選舉爲Controller,主要負責partition管理和副本狀態管理,也會執行類似於重分配partition之類的管理任務。在符合某些特定條件下,Controller下的LeaderSelector會選舉新的Leader,ISR和新的leader_epoch及controller_epoch寫入Zookeeper的相關節點中。同時發起LeaderAndIsrRequest通知所有的replicas。
  2. Leader來維護:Leader有單獨的線程定期檢測ISR中Follower是否脫離ISR,如果發現ISR變化,則會將新的ISR的信息返回到Zookeeper的相關節點中。

歡迎關注,《大數據成神之路》系列文章

歡迎關注,《大數據成神之路》系列文章

歡迎關注,《大數據成神之路》系列文章

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