當前位置:
首頁 > 知識 > Kafka 「不丟消息」 ISR 機制解析

Kafka 「不丟消息」 ISR 機制解析

許多消息都會各種保證自己的產品不會丟消息或者消息丟失概率較小,但是靠譜的很少,而且消息隊列丟消息排查起來是非常麻煩的,所以大多數在使用的過程中都會在上層或者下層建立一種消息核對或者應對丟失的策略。在丟消息這方面,Kafka 算是有著不小的優勢,只要去正確使用,Kafka 基本是不會產生丟失的,並且能做到精確一次處理。

Kafka 交付語義、producer中都提到了消息提交給broker中,基本就不會丟消息了,而這個不丟消息主要是依賴於broker 中的ISR機制。

首先Kafka 消息在broker的存儲形式是以log的形式存在的,打開Kafka的存儲的文件夾時就能發現有.log .index .timeindex 三類文件,其中index、timeindex是索引文件,而.log就是具體的消息的存儲文件。不同的文件存在於不同的分區,這個是由分區選擇器確定的。按照常識,要想保證高可用保證不丟失,最直觀的就是製造冗餘,多做備份,數據互備嘛,Kafka 也是這麼去做的。

在Kafka 中備份日誌文件被稱為replica,replica 又分為leader replica 和follower replica,而follower replica存在的唯一目的就是防止消息丟失,並不參與具體的業務邏輯的交互。只有leader 才參與服務,follower的作用就是充當leader的候補,平時的操作也只有信息同步。ISR (in-sync replica)也就是這組與leader保持同步的replica集合,我們要保證不丟消息,首先要保證ISR的存活(至少有一個備份存活),並且消息提交成功。那存活的概念是什麼呢,就是說不僅需要機器正常,還需要跟上leader的消息進度,當達到一定程度的時候就會認為「非存活」狀態。

具體來看看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的同步進度的。

現在就來看一下之前,broker從收到消息到返迴響應這個黑盒子里發生了什麼。

1、broker 收到producer的請求

2、leader 收到消息,並成功寫入,LEO 值+1

3、broker 將消息推給follower replica,follower 成功寫入 LEO +1

4、所有LEO 寫入後,leader HW +1

5、消息可被消費,並成功響應

這裡具體需要同步完成的follower的數量是由acks參數來確定的,當設定為1的時候僅需要同步給一個follower即可,如果為-1(all),則需要同步所有的follower,如果為0的話就代表不需要同步給follower,記下消息之後立馬返回,這樣的吞吐量是最好的,但是對消息的也就不能保證丟了,其實常規環境對消息丟失要求沒有那麼嚴苛的環境還是可以使用的。常規使用最多的環境應該是設置為1,同步一份就ok了。

ISR(in sync replica)的含義是同步的replica,相對的就有out of sync replica,也就是跟不上同步節奏的replica,現在面臨的有兩個問題,當replica 跟不上進度時該怎麼處理(或原本跟不上節奏的現在又跟上節奏了該如何處理)、如何去判定跟不跟得上節奏。

第一個問題很簡單,跟上節奏就加入ISR,跟不上節奏就踢出ISR。

關鍵是如何判定:

在0.9.0.0之前,Kafka提供了replica lag.max.messages 來控制follower副本最多落後leader副本的消息數量,follower 相對於leader 落後當超過這個數量的時候就判定該follower是失效的,就會踢出ISR,這裡的指的是具體的LEO值。常見的導致同步跟不上的原因主要是下面幾個:

1、新的副本(這是很常見的情況,每個新的副本加入都需要一段信息同步的追趕時期)

2、網路IO等原因,某些機器IO處理速度變慢所導致持續消費落後。

3、進程卡住(Kafka 是Java 寫出來的,Java 進程最容易卡住的問題是不是親切,就是Full GC,及高頻次GC)

對應的Kafka 也針對這些場景提供了一些控制的參數:前面提到的replica.lag.max.message(以數量為標準衡量是否落後),還有以時間為衡量標準的replica.lag.time.max(多久沒有向leader 請求數據)

這些是0.9.0.0之前的版本,這個實現是可以適應大多數環境的,但是存在一個嚴重的缺陷,當qps持續上升,請求打滿之後,很容易造成同步速率下降或者長時間無響應,進而導致很多follower被踢出ISR(在流量高峰時期會挺常見),這就導致使用者需要在不同的場景定製不同的參數配置,但是什麼時候有突發流量什麼時候去配置並且令其生效,這個事兒不現實,所以說Kafka這一點算是一個缺陷吧。

0.9.0.0 之後提供了一個更加適合的方式來解決這個問題,採用Kafka 落後於消費進度的時間長度來判斷是否踢出ISR,這樣有效的避免了在突發流量偶然落後於leader 被不合理的踢出ISR的情況,如果長時間落後於leader 這種情況實際故障是需要去踢的也沒問題,也就有效的避免了ISR的反覆移進移出所帶來的代價。

下一篇關於LEO & HW值的存儲及更新策略 。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 EffectiveCoding 的精彩文章:

TAG:EffectiveCoding |