許多消息都會各種保證自己的產(chǎn)品不會丟消息或者消息丟失概率較小躏鱼,但是靠譜的很少氮采,而且消息隊列丟消息排查起來是非常麻煩的,所以大多數(shù)在使用的過程中都會在上層或者下層建立一種消息核對或者應(yīng)對丟失的策略染苛。在丟消息這方面鹊漠,Kafka 算是有著不小的優(yōu)勢,只要去正確使用茶行,Kafka 基本是不會產(chǎn)生丟失的躯概,并且能做到精確一次處理。
Kafka 交付語義拢军、producer中都提到了消息提交給broker中楞陷,基本就不會丟消息了,而這個不丟消息主要是依賴于broker 中的ISR機制茉唉。
首先Kafka 消息在broker的存儲形式是以log的形式存在的固蛾,打開Kafka的存儲的文件夾時就能發(fā)現(xiàn)有.log .index .timeindex 三類文件,其中index度陆、timeindex是索引文件艾凯,而.log就是具體的消息的存儲文件。不同的文件存在于不同的分區(qū)懂傀,這個是由分區(qū)選擇器確定的趾诗。按照常識,要想保證高可用保證不丟失蹬蚁,最直觀的就是制造冗余恃泪,多做備份,數(shù)據(jù)互備嘛犀斋,Kafka 也是這么去做的贝乎。
在Kafka 中備份日志文件被稱為replica,replica 又分為leader replica 和follower replica叽粹,而follower replica存在的唯一目的就是防止消息丟失览效,并不參與具體的業(yè)務(wù)邏輯的交互。只有l(wèi)eader 才參與服務(wù)虫几,follower的作用就是充當(dāng)leader的候補锤灿,平時的操作也只有信息同步。ISR (in-sync replica)也就是這組與leader保持同步的replica集合辆脸,我們要保證不丟消息但校,首先要保證ISR的存活(至少有一個備份存活),并且消息提交成功啡氢。那存活的概念是什么呢状囱,就是說不僅需要機器正常州刽,還需要跟上leader的消息進(jìn)度,當(dāng)達(dá)到一定程度的時候就會認(rèn)為“非存活”狀態(tài)浪箭。
ISR
broker offset 大致分為:base offset力试、high watemark(HW)愤惰、log end offset(LEO)這個幾個概念非常重要,要是搞不清的話,后面的內(nèi)容基本上就亂了患朱。
base offset
:起始位移伴奥,replica中第一天消息的offset
HW
:replica高水印值碉渡,副本中最新一條已提交消息的位移岗屏。leader 的HW值也就是實際已提交消息的范圍,每個replica都有HW值冻晤,但僅僅leader中的HW才能作為標(biāo)示信息苇羡。什么意思呢,就是說當(dāng)按照參數(shù)標(biāo)準(zhǔn)成功完成消息備份(成功同步給follower replica后)才會更新HW的值鼻弧,代表消息理論上已經(jīng)不會丟失设江,可以認(rèn)為“已提交”。
LEO
:日志末端位移攘轩,也就是replica中下一條待寫入消息的offset叉存,注意哈,是下一條并且是待寫入的度帮,并不是最后一條歼捏。這個LEO個人感覺也就是用來標(biāo)示follower的同步進(jìn)度的。
現(xiàn)在就來看一下之前笨篷,broker從收到消息到返回響應(yīng)這個黑盒子里發(fā)生了什么瞳秽。
1、broker 收到producer的請求
2率翅、leader 收到消息练俐,并成功寫入,LEO 值+1
3安聘、broker 將消息推給follower replica痰洒,follower 成功寫入 LEO +1
…
4瓢棒、所有LEO 寫入后浴韭,leader HW +1
5、消息可被消費脯宿,并成功響應(yīng)
這里具體需要同步完成的follower的數(shù)量是由acks參數(shù)來確定的念颈,當(dāng)設(shè)定為1的時候僅需要同步給一個follower即可,如果為-1(all)连霉,則需要同步所有的follower榴芳,如果為0的話就代表不需要同步給follower嗡靡,記下消息之后立馬返回,這樣的吞吐量是最好的窟感,但是對消息的也就不能保證丟了讨彼,其實常規(guī)環(huán)境對消息丟失要求沒有那么嚴(yán)苛的環(huán)境還是可以使用的。常規(guī)使用最多的環(huán)境應(yīng)該是設(shè)置為1柿祈,同步一份就ok了哈误。
ISR(in sync replica)的含義是同步的replica,相對的就有out of sync replica躏嚎,也就是跟不上同步節(jié)奏的replica蜜自,現(xiàn)在面臨的有兩個問題,當(dāng)replica 跟不上進(jìn)度時該怎么處理(或原本跟不上節(jié)奏的現(xiàn)在又跟上節(jié)奏了該如何處理)卢佣、如何去判定跟不跟得上節(jié)奏重荠。
第一個問題很簡單,跟上節(jié)奏就加入ISR虚茶,跟不上節(jié)奏就踢出ISR戈鲁。
關(guān)鍵是如何判定:
在0.9.0.0之前,Kafka提供了replica lag.max.messages 來控制follower副本最多落后leader副本的消息數(shù)量嘹叫,follower 相對于leader 落后當(dāng)超過這個數(shù)量的時候就判定該follower是失效的荞彼,就會踢出ISR,這里的指的是具體的LEO值待笑。常見的導(dǎo)致同步跟不上的原因主要是下面幾個:
1鸣皂、新的副本(這是很常見的情況,每個新的副本加入都需要一段信息同步的追趕時期)
2暮蹂、網(wǎng)絡(luò)IO等原因寞缝,某些機器IO處理速度變慢所導(dǎo)致持續(xù)消費落后。
3仰泻、進(jìn)程卡拙B健(Kafka 是Java 寫出來的,Java 進(jìn)程最容易卡住的問題是不是親切集侯,就是Full GC被啼,及高頻次GC)
對應(yīng)的Kafka 也針對這些場景提供了一些控制的參數(shù):前面提到的replica.lag.max.message(以數(shù)量為標(biāo)準(zhǔn)衡量是否落后),還有以時間為衡量標(biāo)準(zhǔn)的replica.lag.time.max(多久沒有向leader 請求數(shù)據(jù))
這些是0.9.0.0之前的版本棠枉,這個實現(xiàn)是可以適應(yīng)大多數(shù)環(huán)境的浓体,但是存在一個嚴(yán)重的缺陷,當(dāng)qps持續(xù)上升辈讶,請求打滿之后命浴,很容易造成同步速率下降或者長時間無響應(yīng),進(jìn)而導(dǎo)致很多follower被踢出ISR(在流量高峰時期會挺常見),這就導(dǎo)致使用者需要在不同的場景定制不同的參數(shù)配置生闲,但是什么時候有突發(fā)流量什么時候去配置并且令其生效媳溺,這個事兒不現(xiàn)實,所以說Kafka這一點算是一個缺陷吧碍讯。
0.9.0.0 之后提供了一個更加適合的方式來解決這個問題悬蔽,采用Kafka 落后于消費進(jìn)度的時間長度來判斷是否踢出ISR,這樣有效的避免了在突發(fā)流量偶然落后于leader 被不合理的踢出ISR的情況捉兴,如果長時間落后于leader 這種情況實際故障是需要去踢的也沒問題屯阀,也就有效的避免了ISR的反復(fù)移進(jìn)移出所帶來的代價。
下一篇關(guān)于LEO & HW值的存儲及更新策略