Kafka設計解析-Kafka High Availability

1. Kafka簡介

Kafka是一種分布式的,基于發布/訂閱的消息系統。主要設計目標如下:

  1. 以時間復雜度為O(1)的方式提供消息持久化能力,即使對TB級以上數據也能保證常數時間復雜度的訪問性能
  2. 高吞吐率。即使在非常廉價的商用機器上也能做到單機支持每秒100K條以上消息的傳輸
  3. 支持Kafka Server間的消息分區,及分布式消費,同時保證每個Partition內的消息順序傳輸
  4. 同時支持離線數據處理和實時數據處理
  5. Scale out:支持在線水平擴展

2. Kafka架構

Broker:Kafka集群包含一個或多個服務器,這種服務器被稱為broker

Topic:每條發布到Kafka集群的消息都有一個類別,這個類別被稱為Topic。(物理上不同Topic的消息分開存儲,邏輯上一個Topic的消息雖然保存于一個或多個broker上但用戶只需指定消息的Topic即可生產或消費數據而不必關心數據存于何處)

Partition:Parition是物理上的概念,每個Topic包含一個或多個Partition.

Producer:負責發布消息到Kafka broker

Consumer:消息消費者,向Kafka broker讀取消息的客戶端。

Consumer Group:每個Consumer屬于一個特定的Consumer Group(可為每個Consumer指定group name,若不指定group name則屬于默認的group)。

3. Kafka拓撲結構

http://static.cyblogs.com/KafkaArchitecture.png

如上圖所示,一個典型的Kafka集群中包含若干Producer(可以是web前端產生的Page View,或者是服務器日志,系統CPU、Memory等),若干broker(Kafka支持水平擴展,一般broker數量越多,集群吞吐率越高),若干Consumer Group,以及一個Zookeeper集群。Kafka通過Zookeeper管理集群配置,選舉leader,以及在Consumer Group發生變化時進行rebalance。Producer使用push模式將消息發布到broker,Consumer使用pull模式從broker訂閱并消費消息。

4. Delivery Guarantee

有這么幾種可能的delivery guarantee:

  • At most once 消息可能會丟,但絕不會重復傳輸
  • At least one 消息絕不會丟,但可能會重復傳輸
  • Exactly once 每條消息肯定會被傳輸一次且僅傳輸一次,很多時候這是用戶所想要的。

5. Kafka HA設計解析

5.1 Replica復制算法

? 為了更好的做負載均衡,Kafka盡量將所有的Partition均勻分配到整個集群上。一個典型的部署方式是一個Topic的Partition數量大于Broker的數量。同時為了提高Kafka的容錯能力,也需要將同一個Partition的Replica盡量分散到不同的機器。實際上,如果所有的Replica都在同一個Broker上,那一旦該Broker宕機,該Partition的所有Replica都無法工作,也就達不到HA的效果。同時,如果某個Broker宕機了,需要保證它上面的負載可以被均勻的分配到其它幸存的所有Broker上。

  1. 將所有Broker(假設共n個Broker)和待分配的Partition排序
  2. 將第i個Partition分配到第(i mod n)個Broker上
  3. 將第i個Partition的第j個Replica分配到第((i + j) mod n)個Broker上
5.2 Broker活著的判定

kafka判定broker是否活著,通過以下2個方式:

  1. 和zk的session沒有斷(通過心跳來維系)
  2. follower能及時將leader消息復制過來,不能落后太多(例如默認lag超過4000就會踢出ISR)
5.3 所有Replica都不工作的情況

如果所有副本都出問題,一般有兩種選擇:

  1. 等待ISR中的任一個Replica“活”過來,并且選它作為Leader(一致性好,但是可用性差)
  2. 選擇第一個“活”過來的Replica(不一定是ISR中的)作為Leader(一致性差,但是可用性相比第一種方式好)
5.4 Propagate消息

? Producer在發布消息到某個Partition時,先通過 Metadata (通過 Broker 獲取并且緩存在 Producer 內) 找到該 Partition 的Leader,然后無論該Topic的Replication Factor為多少(也即該Partition有多少個Replica),Producer只將該消息發送到該Partition的Leader。Leader會將該消息寫入其本地Log。每個Follower都從Leader pull數據。這種方式上,Follower存儲的數據順序與Leader保持一致。Follower在收到該消息并寫入其Log后,向Leader發送ACK。一旦Leader收到了ISR中的所有Replica的ACK,該消息就被認為已經commit了,Leader將增加HW并且向Producer發送ACK。
為了提高性能,每個Follower在接收到數據后就立馬向Leader發送ACK,而非等到數據寫入Log中。因此,對于已經commit的消息,Kafka只能保證它被存于多個Replica的內存中,而不能保證它們被持久化到磁盤中,也就不能完全保證異常發生后該條消息一定能被Consumer消費。但考慮到這種場景非常少見,可以認為這種方式在性能和數據持久化上做了一個比較好的平衡。

5.5 ACK前需要保證有多少個備份

? 和大部分分布式系統一樣,Kafka處理失敗需要明確定義一個Broker是否“活著”。對于Kafka而言,Kafka存活包含兩個條件,一是它必須維護與Zookeeper的session(這個通過Zookeeper的Heartbeat機制來實現)。二是Follower必須能夠及時將Leader的消息復制過來,不能“落后太多”。
  Leader會跟蹤與其保持同步的Replica列表,該列表稱為ISR(即in-sync Replica)。如果一個Follower宕機,或者落后太多,Leader將把它從ISR中移除。這里所描述的“落后太多”指Follower復制的消息落后于Leader后的條數超過預定值(該值可在KAFKA_HOME/config/server.properties中通過`replica.lag.max.messages`配置,其默認值是4000)或者Follower超過一定時間(該值可在KAFKA_HOME/config/server.properties中通過replica.lag.time.max.ms來配置,其默認值是10000)未向Leader發送fetch請求。。
  Kafka的復制機制既不是完全的同步復制,也不是單純的異步復制。事實上,同步復制要求所有能工作的Follower都復制完,這條消息才會被認為commit,這種復制方式極大的影響了吞吐率(高吞吐率是Kafka非常重要的一個特性)。而異步復制方式下,Follower異步的從Leader復制數據,數據只要被Leader寫入log就被認為已經commit,這種情況下如果Follower都復制完都落后于Leader,而如果Leader突然宕機,則會丟失數據。而Kafka的這種使用ISR的方式則很好的均衡了確保數據不丟失以及吞吐率。Follower可以批量的從Leader復制數據,這樣極大的提高復制性能(批量寫磁盤),極大減少了Follower與Leader的差距。

6. 分區Leader選舉方法

一般比較容易想到的一個方法是:所有Follower都在Zookeeper上設置一個Watch,一旦Leader宕機,其對應的ephemeral znode會自動刪除,此時所有Follower都嘗試創建該節點,而創建成功者(Zookeeper保證只有一個能創建成功)即是新的Leader,其它Replica即為Follower。

該方法會存在3個問題:

  1. split-brain 這是由Zookeeper的特性引起的,雖然Zookeeper能保證所有Watch按順序觸發,但并不能保證同一時刻所有Replica“看”到的狀態是一樣的,這就可能造成不同Replica的響應不一致
  2. herd effect 如果宕機的那個Broker上的Partition比較多,會造成多個Watch被觸發,造成集群內大量的調整
  3. Zookeeper負載過重 每個Replica都要為此在Zookeeper上注冊一個Watch,當集群規模增加到幾千個Partition時Zookeeper負載會過重。

改進的方法——所有broker中選出一個controller,所有Partition的Leader選舉都由controller決定。controller會將Leader的改變直接通過RPC的方式(比Zookeeper Queue的方式更高效)通知需為此作出響應的Broker。同時controller也負責增刪Topic以及Replica的重新分配。

7. 各組件Failover過程

Broker failover過程
  1. Controller在Zookeeper注冊Watch,一旦有Broker宕機(這是用宕機代表任何讓系統認為其die的情景,包括但不限于機器斷電,網絡不可用,GC導致的Stop The World,進程crash等),其在Zookeeper對應的znode會自動被刪除,Zookeeper會fire Controller注冊的watch,Controller讀取最新的幸存的Broker
  2. Controller決定set_p,該集合包含了宕機的所有Broker上的所有Partition
  3. 對set_p中的每一個Partition

3.1 從/brokers/topics/[topic]/partitions/[partition]/state讀取該Partition當前的ISR

? 3.2 決定該Partition的新Leader。如果當前ISR中有至少一個Replica還幸存,則選擇其中一個作為新Leader,新的ISR則包含當前ISR中所有幸存的Replica。否則選擇該Partition中任意一個幸存的Replica作為新的Leader以及ISR(該場景下可能會有潛在的數據丟失)。如果該Partition的所有Replica都宕機了,則將新的Leader設置為-1。

3.3 將新的Leader,ISR和新的leader_epoch及controller_epoch寫入/brokers/topics/[topic]/partitions/[partition]/state。注意,該操作只有其version在3.1至3.3的過程中無變化時才會執行,否則跳轉到3.1

  1. 直接通過RPC向set_p相關的Broker發送LeaderAndISRRequest命令。Controller可以在一個RPC操作中發送多個命令從而提高效率。

Broker failover順序圖如下所示。

http://static.cyblogs.com/kafka_broker_failover.png
Controller failure過程
  1. Controller在Zookeeper的/brokers/ids節點上注冊Watch。一旦有Broker宕機(本文用宕機代表任何讓Kafka認為其Broker die的情景,包括但不限于機器斷電,網絡不可用,GC導致的Stop The World,進程crash等),其在Zookeeper對應的Znode會自動被刪除,Zookeeper會fire Controller注冊的Watch,Controller即可獲取最新的幸存的Broker列表。
  2. Controller決定set_p,該集合包含了宕機的所有Broker上的所有Partition。
  3. 對set_p中的每一個Partition:
      3.1 從/brokers/topics/[topic]/partitions/[partition]/state讀取該Partition當前的ISR。
      3.2 決定該Partition的新Leader。如果當前ISR中有至少一個Replica還幸存,則選擇其中一個作為新Leader,新的ISR則包含當前ISR中所有幸存的Replica。否則選擇該Partition中任意一個幸存的Replica作為新的Leader以及ISR(該場景下可能會有潛在的數據丟失)。如果該Partition的所有Replica都宕機了,則將新的Leader設置為-1。
      3.3 將新的Leader,ISR和新的leader_epochcontroller_epoch寫入/brokers/topics/[topic]/partitions/[partition]/state。注意,該操作只有Controller版本在3.1至3.3的過程中無變化時才會執行,否則跳轉到3.1。
  4. 直接通過RPC向set_p相關的Broker發送LeaderAndISRRequest命令。Controller可以在一個RPC操作中發送多個命令從而提高效率。
      Broker failover順序圖如下所示。
http://static.cyblogs.com/kafka_broker_failover.png

Partition重新分配

管理工具發出重新分配Partition請求后,會將相應信息寫到/admin/reassign_partitions上,而該操作會觸發ReassignedPartitionsIsrChangeListener,從而通過執行回調函數KafkaController.onPartitionReassignment來完成以下操作:

  1. 將Zookeeper中的AR(Current Assigned Replicas)更新為OAR(Original list of replicas for partition) + RAR(Reassigned replicas)。
  2. 強制更新Zookeeper中的leader epoch,向AR中的每個Replica發送LeaderAndIsrRequest。
  3. 將RAR - OAR中的Replica設置為NewReplica狀態。
  4. 等待直到RAR中所有的Replica都與其Leader同步。
  5. 將RAR中所有的Replica都設置為OnlineReplica狀態。
  6. 將Cache中的AR設置為RAR。
  7. 若Leader不在RAR中,則從RAR中重新選舉出一個新的Leader并發送LeaderAndIsrRequest。若新的Leader不是從RAR中選舉而出,則還要增加Zookeeper中的leader epoch。
  8. 將OAR - RAR中的所有Replica設置為OfflineReplica狀態,該過程包含兩部分。第一,將Zookeeper上ISR中的OAR - RAR移除并向Leader發送LeaderAndIsrRequest從而通知這些Replica已經從ISR中移除;第二,向OAR - RAR中的Replica發送StopReplicaRequest從而停止不再分配給該Partition的Replica。
  9. 將OAR - RAR中的所有Replica設置為NonExistentReplica狀態從而將其從磁盤上刪除。
  10. 將Zookeeper中的AR設置為RAR。
  11. 刪除/admin/reassign_partition

參考文章

如果大家喜歡我的文章,可以關注個人訂閱號。歡迎隨時留言、交流。

簡棧文化服務訂閱號
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容