(0)寫在前面 java
面試大廠時,一旦簡歷上寫了Kafka,幾乎必然會被問到一個問題:說說acks參數對消息持久化的影響?面試
這個acks參數在kafka的使用中,是很是核心以及關鍵的一個參數,決定了不少東西。redis
因此不管是爲了面試仍是實際項目使用,你們都值得看一下這篇文章對Kafka的acks參數的分析,以及背後的原理。架構
(1)如何保證宕機的時候數據不丟失?elasticsearch
若是要想理解這個acks參數的含義,首先就得搞明白kafka的高可用架構原理。分佈式
好比下面的圖裏就是代表了對於每個Topic,咱們均可以設置他包含幾個Partition,每一個Partition負責存儲這個Topic一部分的數據。.net
而後Kafka的Broker集羣中,每臺機器上都存儲了一些Partition,也就存放了Topic的一部分數據,這樣就實現了Topic的數據分佈式存儲在一個Broker集羣上。blog
可是有一個問題,萬一 一個Kafka Broker宕機了,此時上面存儲的數據不就丟失了嗎?kafka
沒錯,這就是一個比較大的問題了,分佈式系統的數據丟失問題,是他首先必需要解決的,一旦說任何一臺機器宕機,此時就會致使數據的丟失。同步
(2)多副本冗餘的高可用機制
因此若是你們去分析任何一個分佈式系統的原理,好比說zookeeper、kafka、redis cluster、elasticsearch、hdfs,等等,其實他都有本身內部的一套多副本冗餘的機制,多副本冗餘幾乎是如今任何一個優秀的分佈式系統都通常要具有的功能。
在kafka集羣中,每一個Partition都有多個副本,其中一個副本叫作leader,其餘的副本叫作follower,以下圖。
如上圖所示,假設一個Topic拆分爲了3個Partition,分別是Partition0,Partiton1,Partition2,此時每一個Partition都有2個副本。
好比Partition0有一個副本是Leader,另一個副本是Follower,Leader和Follower兩個副本是分佈在不一樣機器上的。
這樣的多副本冗餘機制,能夠保證任何一臺機器掛掉,都不會致使數據完全丟失,由於起碼仍是有副本在別的機器上的。
(3)多副本之間數據如何同步?
接着咱們就來看看多個副本之間數據是如何同步的?其實任何一個Partition,只有Leader是對外提供讀寫服務的
也就是說,若是有一個客戶端往一個Partition寫入數據,此時通常就是寫入這個Partition的Leader副本。
而後Leader副本接收到數據以後,Follower副本會不停的給他發送請求嘗試去拉取最新的數據,拉取到本身本地後,寫入磁盤中。以下圖所示:
(4)ISR到底指的是什麼東西?
既然你們已經知道了Partiton的多副本同步數據的機制了,那麼就能夠來看看ISR是什麼了。
ISR全稱是「In-Sync Replicas」,也就是保持同步的副本,他的含義就是,跟Leader始終保持同步的Follower有哪些。
你們能夠想一下 ,若是說某個Follower所在的Broker由於JVM FullGC之類的問題,致使本身卡頓了,沒法及時從Leader拉取同步數據,那麼是否是會致使Follower的數據比Leader要落後不少?
因此這個時候,就意味着Follower已經跟Leader再也不處於同步的關係了。可是隻要Follower一直及時從Leader同步數據,就能夠保證他們是處於同步的關係的。
因此每一個Partition都有一個ISR,這個ISR裏必定會有Leader本身,由於Leader確定數據是最新的,而後就是那些跟Leader保持同步的Follower,也會在ISR裏。
(5)acks參數的含義
鋪墊了那麼多的東西,最後終於能夠進入主題來聊一下acks參數的含義了。
若是你們沒看明白前面的那些副本機制、同步機制、ISR機制,那麼就沒法充分的理解acks參數的含義,這個參數實際上決定了不少重要的東西。
首先這個acks參數,是在KafkaProducer,也就是生產者客戶端裏設置的
也就是說,你往kafka寫數據的時候,就能夠來設置這個acks參數。而後這個參數實際上有三種常見的值能夠設置,分別是:0、1 和 all。
第一種選擇是把acks參數設置爲0,意思就是個人KafkaProducer在客戶端,只要把消息發送出去,無論那條數據有沒有在哪怕Partition Leader上落到磁盤,我就無論他了,直接就認爲這個消息發送成功了。
若是你採用這種設置的話,那麼你必須注意的一點是,可能你發送出去的消息還在半路。結果呢,Partition Leader所在Broker就直接掛了,而後結果你的客戶端還認爲消息發送成功了,此時就會致使這條消息就丟失了。
第二種選擇是設置 acks = 1,意思就是說只要Partition Leader接收到消息並且寫入本地磁盤了,就認爲成功了,無論他其餘的Follower有沒有同步過去這條消息了。
這種設置實際上是kafka默認的設置,你們請注意,劃重點!這是默認的設置
也就是說,默認狀況下,你要是無論acks這個參數,只要Partition Leader寫成功就算成功。
可是這裏有一個問題,萬一Partition Leader剛剛接收到消息,Follower還沒來得及同步過去,結果Leader所在的broker宕機了,此時也會致使這條消息丟失,由於人家客戶端已經認爲發送成功了。
最後一種狀況,就是設置acks=all,這個意思就是說,Partition Leader接收到消息以後,還必需要求ISR列表裏跟Leader保持同步的那些Follower都要把消息同步過去,才能認爲這條消息是寫入成功了。
若是說Partition Leader剛接收到了消息,可是結果Follower沒有收到消息,此時Leader宕機了,那麼客戶端會感知到這個消息沒發送成功,他會重試再次發送消息過去。
此時可能Partition 2的Follower變成Leader了,此時ISR列表裏只有最新的這個Follower轉變成的Leader了,那麼只要這個新的Leader接收消息就算成功了。
(6)最後的思考
acks=all 就能夠表明數據必定不會丟失了嗎?
固然不是,若是你的Partition只有一個副本,也就是一個Leader,任何Follower都沒有,你認爲acks=all有用嗎?
固然沒用了,由於ISR裏就一個Leader,他接收完消息後宕機,也會致使數據丟失。
因此說,這個acks=all,必須跟ISR列表裏至少有2個以上的副本配合使用,起碼是有一個Leader和一個Follower才能夠。
這樣才能保證說寫一條數據過去,必定是2個以上的副本都收到了纔算是成功,此時任何一個副本宕機,不會致使數據丟失。
因此但願你們把這篇文章好好理解一下,對你們出去面試,或者工做中用kafka都是很好的一個幫助。————————————————版權聲明:本文爲CSDN博主「中琦2513」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。原文連接:https://blog.csdn.net/zhongqi2513/java/article/details/90476219