kafka-如何保證訊息的可靠性與一致性

匠心Java發表於2019-01-22

在kafka中主要通過ISR機制來保證訊息的可靠性。 下面通過幾個問題來說明kafka如何來保證訊息可靠性與一致性

在kafka中ISR是什麼?

在zk中會儲存AR(Assigned Replicas)列表,其中包含了分割槽所有的副本,其中 AR = ISR+OSR

  • ISR(in sync replica):是kafka動態維護的一組同步副本,在ISR中有成員存活時,只有這個組的成員才可以成為leader,內部儲存的為每次提交資訊時必須同步的副本(acks = all時),每當leader掛掉時,在ISR集合中選舉出一個follower作為leader提供服務,當ISR中的副本被認為壞掉的時候,會被踢出ISR,當重新跟上leader的訊息資料時,重新進入ISR。
  • OSR(out sync replica): 儲存的副本不必保證必須同步完成才進行確認,OSR內的副本是否同步了leader的資料,不影響資料的提交,OSR內的follower盡力的去同步leader,可能資料版本會落後。
kafka如何控制需要同步多少副本才可以返回確定到生產者訊息才可用?
  • 當寫入到kakfa時,生產者可以選擇是否等待0(只需寫入leader),1(只需同步一個副本) 或 -1(全部副本)的訊息確認(這裡的副本指的是ISR中的副本)。
  • 需要注意的是“所有副本確認”並不能保證全部分配副本已收到訊息。預設情況下,當acks=all時,只要當前所有在同步中的副本(ISR中的副本)收到訊息,就會進行確認。所以Kafka的交付承諾可以這樣理解:對沒有提交成功的訊息不做任何交付保證,而對於ISR中至少有一個存活的完全同步的副本的情況下的“成功提交”的訊息保證不會丟失。
對於kafka節點活著的條件是什麼?
  • 第一點:一個節點必須維持和zk的會話,通過zk的心跳檢測實現
  • 第二點:如果節點是一個slave也就是複製節點,那麼他必須複製leader節點不能太落後。這裡的落後可以指兩種情況
    • 1:資料複製落後,slave節點和leader節點的資料相差較大,這種情況有一個缺點,在生產者突然傳送大量訊息導致網路堵塞後,大量的slav複製受阻,導致資料複製落後被大量的踢出ISR。
    • 2:時間相差過大,指的是slave向leader請求複製的時間距離上次請求相隔時間過大。通過配置replica.lag.time.max就可以配置這個時間引數。這種方式解決了上述第一種方式導致的問題。
kafka分割槽partition掛掉之後如何恢復?

在kafka中有一個partition recovery機制用於恢復掛掉的partition。

每個Partition會在磁碟記錄一個RecoveryPoint(恢復點), 記錄已經flush到磁碟的最大offset。當broker fail 重啟時,會進行loadLogs。 首先會讀取該Partition的RecoveryPoint,找到包含RecoveryPoint點上的segment及以後的segment, 這些segment就是可能沒有完全flush到磁碟segments。然後呼叫segment的recover,重新讀取各個segment的msg,並重建索引。

優點:

  1. 以segment為單位管理Partition資料,方便資料生命週期的管理,刪除過期資料簡單
  2. 在程式崩潰重啟時,加快recovery速度,只需恢復未完全flush到磁碟的segment即可
什麼原因導致副本與leader不同步的呢?
  • 慢副本:在一定週期時間內follower不能追趕上leader。最常見的原因之一是I / O瓶頸導致follower追加複製訊息速度慢於從leader拉取速度。
  • 卡住副本:在一定週期時間內follower停止從leader拉取請求。follower replica卡住了是由於GC暫停或follower失效或死亡。
  • 新啟動副本:當使用者給主題增加副本因子時,新的follower不在同步副本列表中,直到他們完全趕上了leader日誌。

一個partition的follower落後於leader足夠多時,被認為不在同步副本列表或處於滯後狀態。

正如上述所說,現在kafka判定落後有兩種,副本滯後判斷依據是副本落後於leader最大訊息數量(replica.lag.max.messages)或replicas響應partition leader的最長等待時間(replica.lag.time.max.ms)。前者是用來檢測緩慢的副本,而後者是用來檢測失效或死亡的副本

如果ISR內的副本掛掉怎麼辦?
  • 兩種選擇:服務直接不可用一段時間等待ISR中副本恢復(祈禱恢復的副本有資料吧) 或者 直接選用第一個副本(這個副本不一定在ISR中)作為leader,這兩種方法也是在可用性和一致性之間的權衡。
  • 服務不可用方式這種適用在不允許訊息丟失的情況下使用,適用於一致性大於可用性,可以有兩種做法
    • 設定ISR最小同步副本數量,如果ISR的當前數量大於設定的最小同步值,那麼該分割槽才會接受寫入,避免了ISR同步副本過少。如果小於最小值那麼該分割槽將不接收寫入。這個最小值設定只有在acks = all的時候才會生效。
    • 禁用unclean-leader選舉,當isr中的所有副本全部不可用時,不可以使用OSR 中的副本作為leader,直接使服務不可用,直到等到ISR 中副本恢復再進行選舉leader。
  • 直接選擇第一個副本作為leader的方式,適用於可用性大於一致性的場景,這也是kafka在isr中所有副本都死亡了的情況採用的預設處理方式,我們可以通過配置引數unclean.leader.election.enable來禁止這種行為,採用第一種方法。
那麼ISR是如何實現同步的呢?

broker的offset大致分為三種:base offset、high watemark(HW)、log end offset(LEO)

  • base offset:起始位移,replica中第一天訊息的offset
  • HW:replica高水印值,副本中最新一條已提交訊息的位移。leader 的HW值也就是實際已提交訊息的範圍,每個replica都有HW值,但僅僅leader中的HW才能作為標示資訊。什麼意思呢,就是說當按照引數標準成功完成訊息備份(成功同步給follower replica後)才會更新HW的值,代表訊息理論上已經不會丟失,可以認為“已提交”。
  • LEO:日誌末端位移,也就是replica中下一條待寫入訊息的offset,注意哈,是下一條並且是待寫入的,並不是最後一條。這個LEO個人感覺也就是用來標示follower的同步進度的。 所以HW代表已經完成同步的資料的位置,LEO代表已經寫入的最新位置,只有HW位置之前的才是可以被外界訪問的資料。 現在就來看一下之前,broker從收到訊息到返回響應這個黑盒子裡發生了什麼。
    kafka-如何保證訊息的可靠性與一致性
  1. broker 收到producer的請求
  2. leader 收到訊息,併成功寫入,LEO 值+1
  3. broker 將訊息推給follower replica,follower 成功寫入 LEO +1 …
  4. 所有LEO 寫入後,leader HW +1
  5. 訊息可被消費,併成功響應

上述過程從下面的圖便可以看出:

kafka-如何保證訊息的可靠性與一致性

解決上一個問題後,接下來就是kafka如何選用leader呢?

選舉leader常用的方法是多數選舉法,比如Redis等,但是kafka沒有選用多數選舉法,kafka採用的是quorum(法定人數)。

quorum是一種在分散式系統中常用的演算法,主要用來通過資料冗餘來保證資料一致性的投票演算法。在kafka中該演算法的實現就是ISR,在ISR中就是可以被選舉為leader的法定人數。

  • 在leader當機後,只能從ISR列表中選取新的leader,無論ISR中哪個副本被選為新的leader,它都知道HW之前的資料,可以保證在切換了leader後,消費者可以繼續看到HW之前已經提交的資料。
  • HW的截斷機制:選出了新的leader,而新的leader並不能保證已經完全同步了之前leader的所有資料,只能保證HW之前的資料是同步過的,此時所有的follower都要將資料截斷到HW的位置,再和新的leader同步資料,來保證資料一致。 當當機的leader恢復,發現新的leader中的資料和自己持有的資料不一致,此時當機的leader會將自己的資料截斷到當機之前的hw位置,然後同步新leader的資料。當機的leader活過來也像follower一樣同步資料,來保證資料的一致性。

如果感覺這篇文章對您有所幫助,請點選一下喜歡或者關注博主,您的喜歡和關注將是我前進的最大動力!

refer: effectivecoding 官網 部落格

相關文章