一個能干掉90%候選人的Kafka面試連環(huán)炮
1、寫在前面
面試大廠時,一旦簡歷上寫了Kafka,幾乎必然會被問到一個問題:說說acks參數(shù)對消息持久化的影響?
這個acks參數(shù)在kafka的使用中,是非常核心以及關(guān)鍵的一個參數(shù),決定了很多東西。
所以無論是為了面試還是實際項目使用,大家都值得看一下這篇文章對Kafka的acks參數(shù)的分析,以及背后的原理。
2、如何保證宕機的時候數(shù)據(jù)不丟失?
如果要想理解這個acks參數(shù)的含義,首先就得搞明白kafka的高可用架構(gòu)原理。
比如下面的圖里就是表明了對于每一個Topic,我們都可以設(shè)置他包含幾個Partition,每個Partition負(fù)責(zé)存儲這個Topic一部分的數(shù)據(jù)。
然后Kafka的Broker集群中,每臺機器上都存儲了一些Partition,也就存放了Topic的一部分?jǐn)?shù)據(jù),這樣就實現(xiàn)了Topic的數(shù)據(jù)分布式存儲在一個Broker集群上。
但是有一個問題,萬一 一個Kafka Broker宕機了,此時上面存儲的數(shù)據(jù)不就丟失了嗎?
沒錯,這就是一個比較大的問題了,分布式系統(tǒng)的數(shù)據(jù)丟失問題,是他首先必須要解決的,一旦說任何一臺機器宕機,此時就會導(dǎo)致數(shù)據(jù)的丟失。
3、多副本冗余的高可用機制
所以如果大家去分析任何一個分布式系統(tǒng)的原理,比如說zookeeper、kafka、redis cluster、elasticsearch、hdfs,等等,其實他都有自己內(nèi)部的一套多副本冗余的機制,多副本冗余幾乎是現(xiàn)在任何一個優(yōu)秀的分布式系統(tǒng)都一般要具備的功能。
在kafka集群中,每個Partition都有多個副本,其中一個副本叫做leader,其他的副本叫做follower,如下圖。
如上圖所示,假設(shè)一個Topic拆分為了3個Partition,分別是Partition0,Partiton1,Partition2,此時每個Partition都有2個副本。
比如Partition0有一個副本是Leader,另外一個副本是Follower,Leader和Follower兩個副本是分布在不同機器上的。
這樣的多副本冗余機制,可以保證任何一臺機器掛掉,都不會導(dǎo)致數(shù)據(jù)徹底丟失,因為起碼還是有副本在別的機器上的。
4、多副本之間數(shù)據(jù)如何同步?
接著我們就來看看多個副本之間數(shù)據(jù)是如何同步的?其實任何一個Partition,只有Leader是對外提供讀寫服務(wù)的
也就是說,如果有一個客戶端往一個Partition寫入數(shù)據(jù),此時一般就是寫入這個Partition的Leader副本。
然后Leader副本接收到數(shù)據(jù)之后,F(xiàn)ollower副本會不停的給他發(fā)送請求嘗試去拉取最新的數(shù)據(jù),拉取到自己本地后,寫入磁盤中。如下圖所示:
5、ISR到底指的是什么東西?
既然大家已經(jīng)知道了Partiton的多副本同步數(shù)據(jù)的機制了,那么就可以來看看ISR是什么了。
ISR全稱是“In-Sync Replicas”,也就是保持同步的副本,他的含義就是,跟Leader始終保持同步的Follower有哪些。
大家可以想一下 ,如果說某個Follower所在的Broker因為JVM FullGC之類的問題,導(dǎo)致自己卡頓了,無法及時從Leader拉取同步數(shù)據(jù),那么是不是會導(dǎo)致Follower的數(shù)據(jù)比Leader要落后很多?
所以這個時候,就意味著Follower已經(jīng)跟Leader不再處于同步的關(guān)系了。但是只要Follower一直及時從Leader同步數(shù)據(jù),就可以保證他們是處于同步的關(guān)系的。
所以每個Partition都有一個ISR,這個ISR里一定會有Leader自己,因為Leader肯定數(shù)據(jù)是最新的,然后就是那些跟Leader保持同步的Follower,也會在ISR里。
6、acks參數(shù)的含義
鋪墊了那么多的東西,最后終于可以進入主題來聊一下acks參數(shù)的含義了。
如果大家沒看明白前面的那些副本機制、同步機制、ISR機制,那么就無法充分的理解acks參數(shù)的含義,這個參數(shù)實際上決定了很多重要的東西。
首先這個acks參數(shù),是在KafkaProducer,也就是生產(chǎn)者客戶端里設(shè)置的
也就是說,你往kafka寫數(shù)據(jù)的時候,就可以來設(shè)置這個acks參數(shù)。然后這個參數(shù)實際上有三種常見的值可以設(shè)置,分別是:0、1 和 all。
第一種選擇是把acks參數(shù)設(shè)置為0,意思就是我的KafkaProducer在客戶端,只要把消息發(fā)送出去,不管那條數(shù)據(jù)有沒有在哪怕Partition Leader上落到磁盤,我就不管他了,直接就認(rèn)為這個消息發(fā)送成功了。
如果你采用這種設(shè)置的話,那么你必須注意的一點是,可能你發(fā)送出去的消息還在半路。結(jié)果呢,Partition Leader所在Broker就直接掛了,然后結(jié)果你的客戶端還認(rèn)為消息發(fā)送成功了,此時就會導(dǎo)致這條消息就丟失了。
?第二種選擇是設(shè)置 acks = 1,意思就是說只要Partition Leader接收到消息而且寫入本地磁盤了,就認(rèn)為成功了,不管他其他的Follower有沒有同步過去這條消息了。
這種設(shè)置其實是kafka默認(rèn)的設(shè)置,大家請注意,劃重點!這是默認(rèn)的設(shè)置
也就是說,默認(rèn)情況下,你要是不管acks這個參數(shù),只要Partition Leader寫成功就算成功?。
但是這里有一個問題,萬一Partition Leader剛剛接收到消息,F(xiàn)ollower還沒來得及同步過去,結(jié)果Leader所在的broker宕機了,此時也會導(dǎo)致這條消息丟失,因為人家客戶端已經(jīng)認(rèn)為發(fā)送成功了。
?最后一種情況,就是設(shè)置acks=all,這個意思就是說,Partition Leader接收到消息之后,還必須要求ISR列表里跟Leader保持同步的那些Follower都要把消息同步過去,才能認(rèn)為這條消息是寫入成功了。
如果說Partition Leader剛接收到了消息,但是結(jié)果Follower沒有收到消息,此時Leader宕機了,那么客戶端會感知到這個消息沒發(fā)送成功,他會重試再次發(fā)送消息過去。
此時可能Partition 2的Follower變成Leader了,此時ISR列表里只有最新的這個Follower轉(zhuǎn)變成的Leader了,那么只要這個新的Leader接收消息就算成功了。?
7、最后的思考
acks=all 就可以代表數(shù)據(jù)一定不會丟失了嗎?
當(dāng)然不是,如果你的Partition只有一個副本,也就是一個Leader,任何Follower都沒有,你認(rèn)為acks=all有用嗎?
當(dāng)然沒用了,因為ISR里就一個Leader,他接收完消息后宕機,也會導(dǎo)致數(shù)據(jù)丟失。
所以說,這個acks=all,必須跟ISR列表里至少有2個以上的副本配合使用,起碼是有一個Leader和一個Follower才可以。
這樣才能保證說寫一條數(shù)據(jù)過去,一定是2個以上的副本都收到了才算是成功,此時任何一個副本宕機,不會導(dǎo)致數(shù)據(jù)丟失。
所以希望大家把這篇文章好好理解一下,對大家出去面試,或者工作中用kafka都是很好的一個幫助。