這段時間一直斷斷續(xù)續(xù)地在看《Kafka權(quán)威指南》,深深為Kafka里面的架構(gòu)設(shè)計理念所折服。一邊看的同時一邊設(shè)想面試的時候可能會被問到的問題,恰巧前段時間碰到一道阿里的面試題浦辨,Kafka什么情況下會丟消息。這其實是一道很綜合的題目沼沈,既考察了Kafka服務(wù)端的整體架構(gòu)流酬,又抓住了消息數(shù)據(jù)傳遞過程,對于服務(wù)端數(shù)據(jù)同步或重啟列另、生產(chǎn)者生產(chǎn)請求的回包方式芽腾、消費者提交已消費的位置到服務(wù)端的時機可能出現(xiàn)的所有情況,這些很細節(jié)又很關(guān)鍵的步驟進行考察页衙。
一摊滔、深入Kafka
Kafka 的主題被分為多個分區(qū)阴绢,分區(qū)是基本的數(shù)據(jù)塊。分區(qū)存儲在單個磁盤上艰躺, Kafka 可以保證分區(qū)里的事件是有序的呻袭,分區(qū)可以在線(可用),也可以離線(不可用) 腺兴。每個分區(qū)可以有多個副本左电,其中一個副本是首領(lǐng)。
Kafka 的復(fù)制機制和分區(qū)的多副本架構(gòu)是Kafka 可靠性保證的核心页响。Kafka分區(qū)的副本分為兩類:首領(lǐng)副本(也就是首領(lǐng))篓足,負責接收所有的讀請求和寫請求。跟隨者副本(跟隨者)拘泞,負責同步首領(lǐng)副本的所有消息纷纫。
當Kafka初始化時,第一個啟動的broker會在Zookeeper上注冊控制節(jié)點成為控制器陪腌,后來的broker通過watch對象監(jiān)控控制器節(jié)點是否消失辱魁。當控制器broker掛掉時,其他broker監(jiān)測后會像zk發(fā)送申請注冊控制器節(jié)點的請求诗鸭。第一個到達的請求會成為控制器染簇。控制器的主要功能就是負責分區(qū)首領(lǐng)的選舉强岸,一旦有broker宕機锻弓,意味著有些分區(qū)會失去首領(lǐng),這個時候由控制器遍歷這些分區(qū)蝌箍,并確定哪些主題的哪些分區(qū)成為首領(lǐng)青灼。
簡而言之, Kafka 使用Zookeeper 的臨時節(jié)點來選舉控制器妓盲, 并在節(jié)點加入集群或退出集群時通知控制器杂拨。控制器負責在節(jié)點加入或離開集群時進行分區(qū)首領(lǐng)選舉悯衬〉粒控制器使用epoch 來避免“腦裂” 〗畲郑“腦裂”是指兩個節(jié)點同時認為自己是當前的控制器策橘。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?——Kafka權(quán)威指南
物理存儲上,Kafka 的基本存儲單元是分區(qū)娜亿。分區(qū)不會在多個broker 間進行再細分丽已,也不會在同一個broker 的多個磁盤上進行再細分。對于分區(qū)的分配买决,Kafka會遵從的兩個原則沛婴,其一是首領(lǐng)副本和跟隨者副本盡量不要在同一個broker上辰斋,其二是各個分區(qū)的首領(lǐng)副本盡量不要在同一個broker上。文件管理上瘸味,Kafka不會永久保留數(shù)據(jù),會為每個主題設(shè)置數(shù)據(jù)保留期限够挂。但對于當前正在寫入的片段(活躍片段)旁仿,永遠不會被Kafka刪除。需要關(guān)注的另一點是孽糖,Kafka內(nèi)對于每條消息枯冈,都保存了一個長整型的參數(shù),也叫做偏移量办悟。Kafka為每個分區(qū)維護了一個索引尘奏,索引把偏移量映射到片段文件和偏移量在文件里的位置。
二病蛉、Kafka可靠性保證
《Kafka權(quán)威指南》一書總結(jié)了Kafka的幾點基本的可靠性保證機制:
? Kafka 可以保證分區(qū)消息的順序炫加。如果使用同一個生產(chǎn)者往同一個分區(qū)寫入消息,而且消息B 在消息A 之后寫入铺然,那么Kafka 可以保證消息B 的偏移量比消息A 的偏移量大俗孝,而且消費者會先讀取消息A 再讀取消息B 。
? 只有當消息被寫入分區(qū)的所有同步副本時(但不一定要寫入磁盤)魄健,它才被認為是“ 已提交”的赋铝。生產(chǎn)者可以選擇接收不同類型的確認,比如在消息被完全提交時的確認沽瘦,或者在消息被寫入首領(lǐng)副本時的確認革骨,或者在消息被發(fā)送到網(wǎng)絡(luò)時的確認。
? 只要還有一個副本是活躍的析恋,那么已經(jīng)提交的消息就不會丟失良哲。
??消費者只能讀取已經(jīng)提交的消息。
這些基本的保證機制可以用來構(gòu)建可靠的系統(tǒng)绿满,但僅僅依賴它們是無法保證系統(tǒng)完全可靠的臂外。構(gòu)建一個可靠的系統(tǒng)需要作出一些權(quán)衡, Kafka 管理員和開發(fā)者可以在配置參數(shù)上作出權(quán)衡喇颁,從而得到他們想要達到的可靠性漏健。
講回復(fù)制,這里延伸一個同步副本的概念橘霎。如果某個跟隨者副本的消息數(shù)據(jù)和首領(lǐng)副本能保持一致蔫浆,那么這個副本可以認為是一個同步副本。要成為一個同步副本姐叁,主要有以下三個條件:
1.跟隨者副本與zk 6s內(nèi)有保持心跳(秒數(shù)可配)
2. 10s內(nèi)從首領(lǐng)那里獲取消息(可配)
3. 在首領(lǐng)那里獲取過最新消息
broker的配置是保證良好的復(fù)制機制的主要因素瓦盛。這里有三個主要參數(shù):
1.復(fù)制系數(shù)(replication):即要有多少個跟隨者副本洗显。這里一般配3(面試題:為什么是3)。如果是1原环,broker一旦重啟服務(wù)就不可用了挠唆。如果是2,假設(shè)現(xiàn)在屬于控制器的broker重啟嘱吗,那么它會影響到分區(qū)選舉玄组,一旦另一個broker也不可用,那么分區(qū)就可能出現(xiàn)"群龍無首"的情況,導(dǎo)致重復(fù)消息或者丟失消息谒麦。另一方面俄讹,如果是舊版Kafka,1個broker失效會導(dǎo)致集群不穩(wěn)定绕德,導(dǎo)致控制器重啟患膛,也會有服務(wù)不可用的情況發(fā)生。
2.不完全的首領(lǐng)選舉(unclean.leader.election.enable)
當所有跟隨者副本因為broker宕機耻蛇,或者因為網(wǎng)絡(luò)問題導(dǎo)致數(shù)據(jù)不同步時踪蹬,這個時候只有首領(lǐng)副本的數(shù)據(jù)是全量數(shù)據(jù)。跟隨者副本重啟臣咖,這個時候如果首領(lǐng)副本掛了延曙,那么要不要讓未同步的跟隨者副本有資格成為首領(lǐng)副本呢?這個時候要根據(jù)業(yè)務(wù)場景給出選擇亡哄。參數(shù)為false枝缔,那么分區(qū)在舊首領(lǐng)(最后一個同步副本)恢復(fù)之前是不可用的。參數(shù)為true蚊惯,那么可能那么就要承擔丟失數(shù)據(jù)和出現(xiàn)數(shù)據(jù)不一致的風險愿卸。
3.最小同步副本min.insync.replicas
如果要確保已提交的數(shù)據(jù)被寫入不止一個副本,就需要把最少同步副本數(shù)量設(shè)置為大一點的值截型。對于一個包含3 個副本的主題趴荸,如果min.insync.replicas 被設(shè)為2 ,那么至少要存在兩個同步副本才能向分區(qū)寫入數(shù)據(jù)宦焦。如果這個時候只有一個同步副本发钝,那么broker就會停止接受生產(chǎn)者的請求,這個時候該分區(qū)只有只讀狀態(tài)波闹。
除了在broker的配置酝豪,生產(chǎn)者也需要進行可靠性的配置,否則也會有丟消息的風險精堕。這里主要為ack的設(shè)置孵淘,值有0、1和All歹篓,生產(chǎn)者重試次數(shù)的配置瘫证,以及當接收到Kafka服務(wù)端返回錯誤時揉阎,額外的錯誤處理方式”嘲疲可以思考兩個例子:
1.生產(chǎn)者設(shè)置ack=1毙籽,即收到首領(lǐng)副本返回的參數(shù)時認為消息已提交。一旦有消息發(fā)送到首領(lǐng)毡庆,首領(lǐng)在還沒完全同步到其他跟隨者時返回成功后惧财,發(fā)生宕機,重新選舉后選出的首領(lǐng)副本不包含這條生產(chǎn)者的消息扭仁。
2.消息被發(fā)到集群時整好發(fā)生選舉,服務(wù)端拋了一個“LeaderNotAvailableException"異常厅翔,生產(chǎn)者沒處理這類錯誤乖坠,認為消息已提交。
除了broker和生產(chǎn)者刀闷,消費者也有可靠性配置保證消息的消費熊泵。
第一個是group.id,用于標識consumerGroup甸昏。
第二個是auto.offset.reset,這個參數(shù)指定了在沒有偏移量可提交時(比如消費者第l 次啟動時)或者請求的偏移量在broker 上不存在時(第4 章已經(jīng)解釋過這種場景)顽分,消費者會做些什么。這個參數(shù)有兩種配置施蜜。一種是earliest 卒蘸,如果選擇了這種配置,消費者會從分區(qū)的開始位置讀取數(shù)據(jù)翻默,不管偏移量是否有效缸沃,這樣會導(dǎo)致消費者讀取大量的重復(fù)數(shù)據(jù),但可以保證最少的數(shù)據(jù)丟失修械。一種是latest趾牧,如果選擇了這種配置,消費者會從分區(qū)的末尾開始讀取數(shù)據(jù)肯污,這樣可以減少重復(fù)處理消息翘单,但很有可能會錯過一些消息。
第三個是自動提交的參數(shù)(enable.auto.commit和auto.commit.interval.ms),允許自動提交和自動提交的頻度(默認5秒)
如果是顯式提交蹦渣,那么就交由程序去決定什么時候提交哄芜,需要考慮一些事項,比如是否總是在處理完事件后再提交偏移量柬唯、提交頻度是性能和重復(fù)消息數(shù)量之間的權(quán)衡忠烛、再均衡等等。
三权逗、阿里面試題:使用Kafka在什么情況下會丟消息
經(jīng)過上面的鋪墊美尸,我們回過頭來重新審視這道面試題冤议,可以從三個方面(broker、生產(chǎn)者师坎、消費者)入手來回答恕酸。
broker:
1.replication不準確,如1或者2
2.unclean.leader.election.enable設(shè)為true胯陋,那么當有不同步的副本成為首領(lǐng)時蕊温,會丟消息
生產(chǎn)者:
ack=1,就會出現(xiàn)首領(lǐng)在同步好某條消息到所有副本前宕機遏乔,不包含該條消息的副本成為首領(lǐng)义矛,導(dǎo)致丟消息
ack=0,? 生產(chǎn)者不等待服務(wù)端的響應(yīng)就認為已提交所有消息,雖然收獲了客觀的吞吐量盟萨,但是保證不了消息不丟凉翻。
沒有錯誤處理機制或者重試機制,當遇到LeaderNotAvailableException異衬砑ぃ或者網(wǎng)絡(luò)異常時制轰,沒有相應(yīng)的處理方式會導(dǎo)致丟消息。
消費者:
消費者在未消費完所有消息時提交了偏移量胞谭,如果在某條消息還未消費的情況下宕機垃杖,因為服務(wù)端只認偏移量,那么會導(dǎo)致這條消息丟失丈屹。
auto.offset.reset=latest,只取最新的消息调俘,那么舊的消息有可能因為沒消費到而丟失。
消費者再均衡時發(fā)現(xiàn)提交的offset和消費到的offset不一致旺垒,加入已消費的offset在提交的offset之前脉漏,會丟消息,反之則有重復(fù)消息袖牙。
四侧巨、參考文獻:
1.《Kafka權(quán)威指南》