高可靠性分析
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檔案的對應的關系,如下圖:

如上圖,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中,

上圖中有兩個新名詞: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:

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的流轉程序:

由此可見,Kafka的復制機制既不是完全的同步復制,也不是單純的異步復制,事實上,同步復制要求所有能作業的Follower都復制完,這條訊息才會被commit,這種復制方式極大的影響了吞吐率,而異步復制方式下,Follower異步的從Leader復制資料,資料只要被Leader寫入log就被認為已經commit,這種情況下如果follower都還沒有復制完,落后于Leader時,突然Leader宕機,則會丟失資料,而Kafka的這種使用ISR的方式則很好的均衡了確保資料不丟失以及吞吐率,
Kafka的ISR的管理最終都會反饋到Zookeeper節點上,具體位置為:/brokers/topics/[topic]/partitions/[partition]/state,目前有兩個地方會對這個Zookeeper的節點進行維護:
- Controller來維護:Kafka集群中的其中一個Broker會被選舉為Controller,主要負責partition管理和副本狀態管理,也會執行類似于重分配partition之類的管理任務,在符合某些特定條件下,Controller下的LeaderSelector會選舉新的Leader,ISR和新的leader_epoch及controller_epoch寫入Zookeeper的相關節點中,同時發起LeaderAndIsrRequest通知所有的replicas,
- Leader來維護:Leader有單獨的執行緒定期檢測ISR中Follower是否脫離ISR,如果發現ISR變化,則會將新的ISR的資訊回傳到Zookeeper的相關節點中,
歡迎關注,《大資料成神之路》系列文章
歡迎關注,《大資料成神之路》系列文章
歡迎關注,《大資料成神之路》系列文章
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/251377.html
標籤:Java
