微信公眾號(hào):moon聊技術(shù)
關(guān)注選擇“ 星標(biāo) ”, 重磅干貨,第一 時(shí)間送達(dá)!
[如果你覺(jué)得文章對(duì)你有幫助,歡迎關(guān)注,在看,點(diǎn)贊,轉(zhuǎn)發(fā)]

大家好,我是moon,最新一篇面試八股文系列 kafka 篇也出爐了,大家還不卷起來(lái)嗎?
- 1.什么是消息中間件?
- 2.kafka 是什么?有什么作用?
- 3.kafka 的架構(gòu)是怎么樣的?
- 4.Kafka Replicas是怎么管理的?
- 5.如何確定當(dāng)前能讀到哪一條消息?
- 6.生產(chǎn)者發(fā)送消息有哪些模式?
- 7.發(fā)送消息的分區(qū)策略有哪些?
- 8.Kafka 支持讀寫分離嗎?為什么?
- 9.那 Kafka 是怎么去實(shí)現(xiàn)負(fù)載均衡的?
- 10.Kafka 的負(fù)責(zé)均衡會(huì)有什么問(wèn)題呢?
- 11.Kafka 的可靠性是怎么保證的?
- 12.Kafka 的消息消費(fèi)方式有哪些?
- 13.分區(qū)再分配是做什么的?解決了什么問(wèn)題?
- 14.副本 leader 是怎么選舉的?
- 15.分區(qū)數(shù)越多越好嗎?吞吐量就會(huì)越高嗎?
- 16.如何增強(qiáng)消費(fèi)者的消費(fèi)能力?
- 17.消費(fèi)者與 topic 的分區(qū)分配策略有哪些?
- 18.kafka 控制器是什么?有什么作用
- 19.kafka 控制器是怎么進(jìn)行選舉的?
- 20.kafka 為什么這么快?
- 21.什么情況下 kafka 會(huì)丟失消息?
1.什么是消息中間件?
消息中間件是基于隊(duì)列與消息傳遞技術(shù),在網(wǎng)絡(luò)環(huán)境中為應(yīng)用系統(tǒng)提供同步或異步、可靠的消息傳輸?shù)闹涡攒浖到y(tǒng)。
消息中間件利用高效可靠的消息傳遞機(jī)制進(jìn)行平臺(tái)無(wú)關(guān)的數(shù)據(jù)交流,并基于數(shù)據(jù)通信來(lái)進(jìn)行分布式系統(tǒng)的集成。通過(guò)提供消息傳遞和消息排隊(duì)模型,它可以在分布式環(huán)境下擴(kuò)展進(jìn)程間的通信。
2.kafka 是什么?有什么作用?
Kafka 是一個(gè)分布式的流式處理平臺(tái),它以高吞吐、可持久化、可水平擴(kuò)展、支持流數(shù)據(jù)處理等多種特性而被廣泛使用

主要功能體現(xiàn)于三點(diǎn):
- 消息系統(tǒng):kafka與傳統(tǒng)的消息中間件都具備系統(tǒng)解耦、冗余存儲(chǔ)、流量削峰、緩沖、異步通信、擴(kuò)展性、可恢復(fù)性等功能。與此同時(shí),kafka還提供了大多數(shù)消息系統(tǒng)難以實(shí)現(xiàn)的消息順序性保障及回溯性消費(fèi)的功能。
- 存儲(chǔ)系統(tǒng):kafka把消息持久化到磁盤,相比于其他基于內(nèi)存存儲(chǔ)的系統(tǒng)而言,有效的降低了消息丟失的風(fēng)險(xiǎn)。這得益于其消息持久化和多副本機(jī)制。也可以將kafka作為長(zhǎng)期的存儲(chǔ)系統(tǒng)來(lái)使用,只需要把對(duì)應(yīng)的數(shù)據(jù)保留策略設(shè)置為“永久”或啟用主題日志壓縮功能。
- 流式處理平臺(tái):kafka為流行的流式處理框架提供了可靠的數(shù)據(jù)來(lái)源,還提供了一個(gè)完整的流式處理框架,比如窗口、連接、變換和聚合等各類操作。
3.kafka 的架構(gòu)是怎么樣的?

一個(gè)典型的 kafka 體系架構(gòu)包括若干 Producer、若干 Consumer、以及一個(gè) Zookeeper 集群(在2.8.0版本中移,除了 Zookeeper,通過(guò) KRaft 進(jìn)行自己的集群管理)
Producer 將消息發(fā)送到 Broker,Broker 負(fù)責(zé)將受到的消息存儲(chǔ)到磁盤中,而 Consumer 負(fù)責(zé)從 Broker 訂閱并消費(fèi)消息。
Kafka 基本概念:
- Producer :生產(chǎn)者,負(fù)責(zé)將消息發(fā)送到 Broker
- Consumer :消費(fèi)者,從 Broker 接收消息
- Consumer Group :消費(fèi)者組,由多個(gè) Consumer 組成。消費(fèi)者組內(nèi)每個(gè)消費(fèi)者負(fù)責(zé)消費(fèi)不同分區(qū)的數(shù)據(jù),一個(gè)分區(qū)只能由一個(gè)組內(nèi)消費(fèi)者消費(fèi);消費(fèi)者組之間互不影響。所有的消費(fèi)者都屬于某個(gè)消費(fèi)者組,即消費(fèi)者組是邏輯上的一個(gè)訂閱者。
- Broker :可以看做一個(gè)獨(dú)立的 Kafka 服務(wù)節(jié)點(diǎn)或 Kafka 服務(wù)實(shí)例。如果一臺(tái)服務(wù)器上只部署了一個(gè) Kafka 實(shí)例,那么我們也可以將 Broker 看做一臺(tái) Kafka 服務(wù)器。
- Topic :一個(gè)邏輯上的概念,包含很多 Partition,同一個(gè) Topic 下的 Partiton 的消息內(nèi)容是不相同的。
- Partition :為了實(shí)現(xiàn)擴(kuò)展性,一個(gè)非常大的 topic 可以分布到多個(gè) broker 上,一個(gè) topic 可以分為多個(gè) partition,每個(gè) partition 是一個(gè)有序的隊(duì)列。
- Replica :副本,同一分區(qū)的不同副本保存的是相同的消息,為保證集群中的某個(gè)節(jié)點(diǎn)發(fā)生故障時(shí),該節(jié)點(diǎn)上的 partition 數(shù)據(jù)不丟失,且 kafka 仍然能夠繼續(xù)工作,kafka 提供了副本機(jī)制,一個(gè) topic 的每個(gè)分區(qū)都有若干個(gè)副本,一個(gè) leader 和若干個(gè) follower。
- Leader :每個(gè)分區(qū)的多個(gè)副本中的"主副本",生產(chǎn)者以及消費(fèi)者只與 Leader 交互。
- Follower :每個(gè)分區(qū)的多個(gè)副本中的"從副本",負(fù)責(zé)實(shí)時(shí)從 Leader 中同步數(shù)據(jù),保持和 Leader 數(shù)據(jù)的同步。Leader 發(fā)生故障時(shí),從 Follower 副本中重新選舉新的 Leader 副本對(duì)外提供服務(wù)。
4.Kafka Replicas是怎么管理的?

- AR:分區(qū)中的所有 Replica 統(tǒng)稱為 AR
- ISR:所有與 Leader 副本保持一定程度同步的Replica(包括 Leader 副本在內(nèi))組成 ISR
- OSR:與 Leader 副本同步滯后過(guò)多的 Replica 組成了 OSR
Leader 負(fù)責(zé)維護(hù)和跟蹤 ISR 集合中所有 Follower 副本的滯后狀態(tài),當(dāng) Follower 副本落后過(guò)多時(shí),就會(huì)將其放入 OSR 集合,當(dāng) Follower 副本追上了 Leader 的進(jìn)度時(shí),就會(huì)將其放入 ISR 集合。
默認(rèn)情況下,只有 ISR 中的副本才有資格晉升為 Leader。
5.如何確定當(dāng)前能讀到哪一條消息?
分區(qū)相當(dāng)于一個(gè)日志文件,我們先簡(jiǎn)單介紹幾個(gè)概念

如上圖是一個(gè)分區(qū)日志文件
- 標(biāo)識(shí)共有7條消息,offset (消息偏移量)分別是0~6
- 0 代表這個(gè)日志文件的開始
- HW(High Watermark) 為4,0~3 代表這個(gè)日志文件可以消費(fèi)的區(qū)間,消費(fèi)者只能消費(fèi)到這四條消息
- LEO 代表即將要寫入消息的偏移量 offset
分區(qū) ISR 集合中的每個(gè)副本都會(huì)維護(hù)自己的 LEO,而 ISR 集合中最小的LEO 即為分區(qū)的 HW

如上圖: 三個(gè)分區(qū)副本都是 ISR集合當(dāng)中的,最小的 LEO 為 3,就代表分區(qū)的 HW 為3,所以當(dāng)前分區(qū)只能消費(fèi)到 0~2 之間的三條數(shù)據(jù),如下圖

6.生產(chǎn)者發(fā)送消息有哪些模式?
總共有三種模式
-
1.發(fā)后即忘(fire-and-forget)
- 它只管往 Kafka 里面發(fā)送消息,但是不關(guān)心消息是否正確到達(dá),這種方式的效率最高,但是可靠性也最差,比如當(dāng)發(fā)生某些不可充實(shí)異常的時(shí)候會(huì)造成消息的丟失
-
2.同步(sync)
- producer.send()返回一個(gè)Future對(duì)象,調(diào)用get()方法變回進(jìn)行同步等待,就知道消息是否發(fā)送成功,發(fā)送一條消息需要等上個(gè)消息發(fā)送成功后才可以繼續(xù)發(fā)送
-
3.異步(async)
- Kafka支持 producer.send() 傳入一個(gè)回調(diào)函數(shù),消息不管成功或者失敗都會(huì)調(diào)用這個(gè)回調(diào)函數(shù),這樣就算是異步發(fā)送,我們也知道消息的發(fā)送情況,然后再回調(diào)函數(shù)中選擇記錄日志還是重試都取決于調(diào)用方
7.發(fā)送消息的分區(qū)策略有哪些?

- 1.輪詢:依次將消息發(fā)送該topic下的所有分區(qū),如果在創(chuàng)建消息的時(shí)候 key 為 null,Kafka 默認(rèn)采用這種策略。
- 2.key 指定分區(qū):在創(chuàng)建消息是 key 不為空,并且使用默認(rèn)分區(qū)器,Kafka 會(huì)將 key 進(jìn)行 hash,然后根據(jù)hash值映射到指定的分區(qū)上。這樣的好處是 key 相同的消息會(huì)在一個(gè)分區(qū)下,Kafka 并不能保證全局有序,但是在每個(gè)分區(qū)下的消息是有序的,按照順序存儲(chǔ),按照順序消費(fèi)。在保證同一個(gè) key 的消息是有序的,這樣基本能滿足消息的順序性的需求。但是如果 partation 數(shù)量發(fā)生變化,那就很難保證 key 與分區(qū)之間的映射關(guān)系了。
- 3.自定義策略:實(shí)現(xiàn) Partitioner 接口就能自定義分區(qū)策略。
- 4.指定 Partiton 發(fā)送
8.Kafka 支持讀寫分離嗎?為什么?
Kafka 是不支持讀寫分離的,那么讀寫分離的好處是什么?主要就是讓一個(gè)節(jié)點(diǎn)去承擔(dān)另一個(gè)節(jié)點(diǎn)的負(fù)載壓力,也就是能做到一定程度的負(fù)載均衡,而且 Kafka 不通過(guò)讀寫分離也可以一定程度上去實(shí)現(xiàn)負(fù)載均衡。
但是對(duì)于 Kafka 的架構(gòu)來(lái)說(shuō),讀寫分離有兩個(gè)很大的缺點(diǎn)

- 1.數(shù)據(jù)不一致的問(wèn)題:讀寫分離必然涉及到數(shù)據(jù)的同步,只要是不同節(jié)點(diǎn)之間的數(shù)據(jù)同步,必然會(huì)有數(shù)據(jù)不一致的問(wèn)題存在。
- 2.延時(shí)問(wèn)題:由于 Kafka 獨(dú)特的數(shù)據(jù)處理方式,導(dǎo)致如果將數(shù)據(jù)從一個(gè)節(jié)點(diǎn)同步到另一個(gè)節(jié)點(diǎn)必然會(huì)經(jīng)過(guò)主節(jié)點(diǎn)磁盤和從節(jié)點(diǎn)磁盤,對(duì)一些延時(shí)性要求較高的應(yīng)用來(lái)說(shuō),并不太適用
9.那 Kafka 是怎么去實(shí)現(xiàn)負(fù)載均衡的?
Kafka 的負(fù)責(zé)均衡主要是通過(guò)分區(qū)來(lái)實(shí)現(xiàn)的,我們知道 Kafka 是主寫主讀的架構(gòu),如下圖:

共三個(gè) broker ,里面各有三個(gè)副本,總共有三個(gè) partation, 深色的是 leader,淺色的是 follower,上下灰色分別代表生產(chǎn)者和消費(fèi)者,虛線代表 follower 從 leader 拉取消息。
我們從這張圖就可以很明顯的看出來(lái),每個(gè) broker 都有消費(fèi)者拉取消息,每個(gè) broker 也都有生產(chǎn)者發(fā)送消息,每個(gè) broker 上的讀寫負(fù)載都是一樣的,這也說(shuō)明了 kafka 獨(dú)特的架構(gòu)方式可以通過(guò)主寫主讀來(lái)實(shí)現(xiàn)負(fù)載均衡。
10.Kafka 的負(fù)責(zé)均衡會(huì)有什么問(wèn)題呢?
kafka的負(fù)載均衡在絕對(duì)理想的狀況下可以實(shí)現(xiàn),但是會(huì)有某些情況出現(xiàn)一定程度上的負(fù)載不均衡
- 1.broker 端分配不均:當(dāng)創(chuàng)建 topic 的時(shí)候可能會(huì)出現(xiàn)某些 broker 分配到的分區(qū)數(shù)多,而有些 broker 分配的分區(qū)少,這就導(dǎo)致了 leader 多副本不均。
- 2.生產(chǎn)者寫入消息不均:生產(chǎn)者可能只對(duì)某些 broker 中的 leader 副本進(jìn)行大量的寫入操作,而對(duì)其他的 leader 副本不聞不問(wèn)。
- 3.消費(fèi)者消費(fèi)不均:消費(fèi)者可能只對(duì)某些 broker 中的 leader 副本進(jìn)行大量的拉取操作,而對(duì)其他的 leader 副本不聞不問(wèn)。
- 4.leader 副本切換不均:當(dāng)主從副本切換或者分區(qū)副本進(jìn)行了重分配后,可能會(huì)導(dǎo)致各個(gè) broker 中的 leader 副本分配不均勻。
11.Kafka 的可靠性是怎么保證的?

1.acks
這個(gè)參數(shù)用來(lái)指定分區(qū)中有多少個(gè)副本收到這條消息,生產(chǎn)者才認(rèn)為這條消息是寫入成功的,這個(gè)參數(shù)有三個(gè)值:
- 1.acks = 1,默認(rèn)為1。生產(chǎn)者發(fā)送消息,只要 leader 副本成功寫入消息,就代表成功。這種方案的問(wèn)題在于,當(dāng)返回成功后,如果 leader 副本和 follower 副本還沒(méi)有來(lái)得及同步,leader 就崩潰了,那么在選舉后新的 leader 就沒(méi)有這條消息,也就丟失了。
- 2.acks = 0。生產(chǎn)者發(fā)送消息后直接算寫入成功,不需要等待響應(yīng)。這個(gè)方案的問(wèn)題很明顯,只要服務(wù)端寫消息時(shí)出現(xiàn)任何問(wèn)題,都會(huì)導(dǎo)致消息丟失。
- 3.acks = -1 或 acks = all。生產(chǎn)者發(fā)送消息后,需要等待 ISR 中的所有副本都成功寫入消息后才能收到服務(wù)端的響應(yīng)。毫無(wú)疑問(wèn)這種方案的可靠性是最高的,但是如果 ISR 中只有l(wèi)eader 副本,那么就和 acks = 1 毫無(wú)差別了。
2.消息發(fā)送的方式
第6問(wèn)中我們提到了生產(chǎn)者發(fā)送消息有三種方式,發(fā)完即忘,同步和異步。我們可以通過(guò)同步或者異步獲取響應(yīng)結(jié)果,失敗做重試來(lái)保證消息的可靠性。
3.手動(dòng)提交位移
默認(rèn)情況下,當(dāng)消費(fèi)者消費(fèi)到消息后,就會(huì)自動(dòng)提交位移。但是如果消費(fèi)者消費(fèi)出錯(cuò),沒(méi)有進(jìn)入真正的業(yè)務(wù)處理,那么就可能會(huì)導(dǎo)致這條消息消費(fèi)失敗,從而丟失。我們可以開啟手動(dòng)提交位移,等待業(yè)務(wù)正常處理完成后,再提交offset。
4.通過(guò)副本 LEO 來(lái)確定分區(qū) HW
可參考第五問(wèn)
12.Kafka 的消息消費(fèi)方式有哪些?
一般消息消費(fèi)有兩種模式,推和拉。Kafka的消費(fèi)是屬于拉模式的,而此模式的消息消費(fèi)方式有兩種,點(diǎn)對(duì)點(diǎn)和發(fā)布訂閱。

- 1.點(diǎn)對(duì)點(diǎn):如果所有消費(fèi)者屬于同一個(gè)消費(fèi)組,那么所有的消息都會(huì)被均勻的投遞給每一個(gè)消費(fèi)者,每條消息只會(huì)被其中一個(gè)消費(fèi)者消費(fèi)。

- 2.發(fā)布訂閱:如果所有消費(fèi)者屬于不同的消費(fèi)組,那么所有的消息都會(huì)被投遞給每一個(gè)消費(fèi)者,每個(gè)消費(fèi)者都會(huì)收到該消息。
13.分區(qū)再分配是做什么的?解決了什么問(wèn)題?
分區(qū)再分配主要是用來(lái)維護(hù) kafka 集群的負(fù)載均衡
既然是分區(qū)再分配,那么 kafka 分區(qū)有什么問(wèn)題呢?

-
問(wèn)題1:當(dāng)集群中的一個(gè)節(jié)點(diǎn)下線了
- 如果該節(jié)點(diǎn)的分區(qū)是單副本的,那么分區(qū)將會(huì)變得不可用
- 如果是多副本的,就會(huì)進(jìn)行 leader 選舉,在其他機(jī)器上選舉出新的 leader
kafka 并不會(huì)將這些失效的分區(qū)遷移到其他可用的 broker 上,這樣就會(huì)影響集群的負(fù)載均衡,甚至也會(huì)影響服務(wù)的可靠性和可用性

- 問(wèn)題2:當(dāng)集群新增 broker 時(shí),只有新的主題分區(qū)會(huì)分配在該 broker 上,而老的主題分區(qū)不會(huì)分配在該 broker 上,就造成了老節(jié)點(diǎn)和新節(jié)點(diǎn)之間的負(fù)載不均衡。
為了解決該問(wèn)題就出現(xiàn)了分區(qū)再分配,它可以在集群擴(kuò)容,broker 失效的場(chǎng)景下進(jìn)行分區(qū)遷移。
分區(qū)再分配的原理就是通化控制器給分區(qū)新增新的副本,然后通過(guò)網(wǎng)絡(luò)把舊的副本數(shù)據(jù)復(fù)制到新的副本上,在復(fù)制完成后,將舊副本清除。 當(dāng)然,為了不影響集群正常的性能,在此復(fù)制期間還會(huì)有一些列保證性能的操作,比如復(fù)制限流。
14.副本 leader 是怎么選舉的?
當(dāng)分區(qū) leader 節(jié)點(diǎn)崩潰時(shí),其中一個(gè) follower 節(jié)點(diǎn)會(huì)成為新的 leader 節(jié)點(diǎn),這樣會(huì)導(dǎo)致集群的負(fù)載不均衡,從而影響服務(wù)的健壯性和穩(wěn)定性。
如下:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">Topic: test Partation:0 Leader:1 Replicas:1,2,0 Isr:1,2,0 Topic: test Partation:1 Leader:2 Replicas:2,0,1 Isr:2,0,1 Topic: test Partation:2 Leader:0 Replicas:0,1,2 Isr:0,1,2 </pre>
我們可以看到
- 0 分區(qū)有 1 個(gè) leader
- 1 分區(qū)有 2 個(gè) leader
- 2 分區(qū)有 0 個(gè) leader
如果此時(shí)中間的節(jié)點(diǎn)重啟
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">Topic: test Partation:0 Leader:1 Replicas:1,2,0 Isr:1,0,2 Topic: test Partation:1 Leader:0 Replicas:2,0,1 Isr:0,1,2 Topic: test Partation:2 Leader:0 Replicas:0,1,2 Isr:0,1,2 </pre>
我們又可以看到:
- 0 分區(qū)有 1 個(gè) leader
- 1 分區(qū)有 0 個(gè) leader
- 2 分區(qū)有 0 個(gè) leader
我們會(huì)發(fā)現(xiàn),原本 1 分區(qū)有兩個(gè) ledaer,經(jīng)過(guò)重啟后 leader 都消失了,如此就負(fù)載不均衡了。
為了解決這種問(wèn)題,就引入了優(yōu)先副本的概念
優(yōu)先副本就是說(shuō)在 AR 集合中的第一個(gè)副本。比如分區(qū) 2 的 AR 為 0,1,2,那么分區(qū) 2 的優(yōu)先副本就為0。理想情況下優(yōu)先副本就是 leader 副本。優(yōu)先副本選舉就是促使優(yōu)先副本成為 leader 副本,從而維護(hù)集群的負(fù)載均衡。
15.分區(qū)數(shù)越多越好嗎?吞吐量就會(huì)越高嗎?
一般類似于這種問(wèn)題的答案,都是持否定態(tài)度的。
但是可以說(shuō),在一定條件下,分區(qū)數(shù)的數(shù)量是和吞吐量成正比的,分區(qū)數(shù)和性能也是成正比的。
那么為什么說(shuō)超過(guò)了一定限度,就會(huì)對(duì)性能造成影響呢?原因如下:

1.客戶端/服務(wù)器端需要使用的內(nèi)存就越多
- 服務(wù)端在很多組件中都維護(hù)了分區(qū)級(jí)別的緩存,分區(qū)數(shù)越大,緩存成本也就越大。
- 消費(fèi)端的消費(fèi)線程數(shù)是和分區(qū)數(shù)掛鉤的,分區(qū)數(shù)越大消費(fèi)線程數(shù)也就越多,線程的開銷成本也就越大
- 生產(chǎn)者發(fā)送消息有緩存的概念,會(huì)為每個(gè)分區(qū)緩存消息,當(dāng)積累到一定程度或者時(shí)間時(shí)會(huì)將消息發(fā)送到分區(qū),分區(qū)越多,這部分的緩存也就越大
2.文件句柄的開銷
每個(gè) partition 都會(huì)對(duì)應(yīng)磁盤文件系統(tǒng)的一個(gè)目錄。在 Kafka 的數(shù)據(jù)日志文件目錄中,每個(gè)日志數(shù)據(jù)段都會(huì)分配兩個(gè)文件,一個(gè)索引文件和一個(gè)數(shù)據(jù)文件。每個(gè) broker 會(huì)為每個(gè)日志段文件打開一個(gè) index 文件句柄和一個(gè)數(shù)據(jù)文件句柄。因此,隨著 partition 的增多,所需要保持打開狀態(tài)的文件句柄數(shù)也就越多,最終可能超過(guò)底層操作系統(tǒng)配置的文件句柄數(shù)量限制。
3.越多的分區(qū)可能增加端對(duì)端的延遲
Kafka 會(huì)將分區(qū) HW 之前的消息暴露給消費(fèi)者。分區(qū)越多則副本之間的同步數(shù)量就越多,在默認(rèn)情況下,每個(gè) broker 從其他 broker 節(jié)點(diǎn)進(jìn)行數(shù)據(jù)副本復(fù)制時(shí),該 broker 節(jié)點(diǎn)只會(huì)為此工作分配一個(gè)線程,該線程需要完成該 broker 所有 partition 數(shù)據(jù)的復(fù)制。
4.降低高可用性
在第 13 問(wèn)我們提到了分區(qū)再分配,會(huì)將數(shù)據(jù)復(fù)制到另一份副本當(dāng)中,分區(qū)數(shù)量越多,那么恢復(fù)時(shí)間也就越長(zhǎng),而如果發(fā)生宕機(jī)的 broker 恰好是 controller 節(jié)點(diǎn)時(shí):在這種情況下,新 leader 節(jié)點(diǎn)的選舉過(guò)程在 controller 節(jié)點(diǎn)恢復(fù)到新的 broker 之前不會(huì)啟動(dòng)。controller 節(jié)點(diǎn)的錯(cuò)誤恢復(fù)將會(huì)自動(dòng)地進(jìn)行,但是新的 controller 節(jié)點(diǎn)需要從 zookeeper 中讀取每一個(gè) partition 的元數(shù)據(jù)信息用于初始化數(shù)據(jù)。例如,假設(shè)一個(gè)Kafka 集群存在 10000個(gè)partition,從 zookeeper 中恢復(fù)元數(shù)據(jù)時(shí)每個(gè) partition 大約花費(fèi) 2 ms,則 controller 的恢復(fù)將會(huì)增加約 20 秒的不可用時(shí)間窗口。
16.如何增強(qiáng)消費(fèi)者的消費(fèi)能力?
- 1.可以考慮增加 topic 的分區(qū)數(shù),并且同時(shí)提升消費(fèi)組的消費(fèi)者數(shù)量,消費(fèi)者數(shù)=分區(qū)數(shù)。
- 2.如果是消費(fèi)者消費(fèi)不及時(shí),可以采用多線程的方式進(jìn)行消費(fèi),并且優(yōu)化業(yè)務(wù)方法流程,同樣的分區(qū)數(shù),為什么人家并發(fā)那么高,你的就不行??
17.消費(fèi)者與 topic 的分區(qū)分配策略有哪些?

1.RangeAssignor 分配策略
該分配策略是按照消費(fèi)者總數(shù)和分區(qū)總數(shù)進(jìn)行整除運(yùn)算來(lái)獲得一個(gè)跨度,然后分區(qū)按照跨度來(lái)進(jìn)行平均分配,盡可能保證分區(qū)均勻的分配給所有的消費(fèi)者。
對(duì)于每個(gè) topic,該策略會(huì)講消費(fèi)者組內(nèi)所有訂閱這個(gè)主題的消費(fèi)者按照名稱的字典順序排序,然后為每個(gè)消費(fèi)者劃分固定過(guò)的區(qū)域,如果不夠平均分配,那么字典排序考前的就會(huì)多分配一個(gè)分區(qū)。
比如 2 個(gè)消費(fèi)者屬于一個(gè)消費(fèi)者組,有 2 個(gè) topic t1,t2,每個(gè) topic 都有 3 個(gè)分區(qū),p1,p2,p3,那么分配的情況如下:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">消費(fèi)者A:t0-p0,t0-p1,t1-p0,t1-p1, 消費(fèi)者B:t0-p2,t1-p2 </pre>
這樣就會(huì)出現(xiàn)非配不均勻的情況
2.RoundRobinAssignor 分配策略
該分配策略是按將消費(fèi)者組內(nèi)所有消費(fèi)者及消費(fèi)者訂閱的所有主題的分區(qū)按照字典排序,然后通過(guò)輪詢的方式分配給每個(gè)消費(fèi)者。
比如有 3 個(gè)消費(fèi)者 A,B,C,訂閱了 3 個(gè) topic ,t0,t1,t2,每個(gè) topic 各有 3 個(gè)分區(qū) p0,p1,p2。 如果 A 訂閱了 t0,B 訂閱了 t0 和 t1,C 訂閱了 t0,t1,t2,那么分配的情況如下:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">消費(fèi)者A:t0-p0 消費(fèi)者B:t1-p0 消費(fèi)者C:t1-p1,t2-p0,t2-p1,t2-p2 </pre>
這樣也會(huì)出現(xiàn)分配不均勻的情況,按照訂閱情況來(lái)講完全可以吧 t1p1 分配給消費(fèi)者B
3.StickyAssignor分配策略
這種分配策略有兩個(gè)目的
- 1.分區(qū)的分配要盡可能的均勻
- 2.分區(qū)的分配盡可能的與上次分配的保持相同。
當(dāng)兩者發(fā)生沖突時(shí),第一個(gè)目標(biāo)優(yōu)先于第二個(gè)目標(biāo)。
假設(shè)消費(fèi)組內(nèi)有3個(gè)消費(fèi)者:C0、C1、C2
它們都訂閱了4個(gè)主題:t0、t1、t2、t3
并且每個(gè)主題有2個(gè)分區(qū),也就是說(shuō)整個(gè)消費(fèi)組訂閱了,t0p0、t0p1、t1p0、t1p1、t2p0、t2p1、t3p0、t3p1 這8個(gè)分區(qū)
最終的分配結(jié)果如下:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`消費(fèi)者C0:t0p0、t1p1、t3p0
消費(fèi)者C1:t0p1、t2p0、t3p1
消費(fèi)者C2:t1p0、t2p1` </pre>
這樣初看上去似乎與采用RoundRobinAssignor策略所分配的結(jié)果相同
此時(shí)假設(shè)消費(fèi)者C1脫離了消費(fèi)組,那么消費(fèi)組就會(huì)執(zhí)行再平衡操作,進(jìn)而消費(fèi)分區(qū)會(huì)重新分配。如果采用RoundRobinAssignor策略,那么此時(shí)的分配結(jié)果如下:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`消費(fèi)者C0:t0p0、t1p0、t2p0、t3p0
消費(fèi)者C2:t0p1、t1p1、t2p1、t3p1` </pre>
如分配結(jié)果所示,RoundRobinAssignor策略會(huì)按照消費(fèi)者C0和C2進(jìn)行重新輪詢分配。而如果此時(shí)使用的是StickyAssignor策略,那么分配結(jié)果為:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`消費(fèi)者C0:t0p0、t1p1、t3p0、t2p0
消費(fèi)者C2:t1p0、t2p1、t0p1、t3p1` </pre>
可以看到分配結(jié)果中保留了上一次分配中對(duì)于消費(fèi)者C0和C2的所有分配結(jié)果,并將原來(lái)消費(fèi)者C1的“負(fù)擔(dān)”分配給了剩余的兩個(gè)消費(fèi)者C0和C2,最終C0和C2的分配還保持了均衡。
如果發(fā)生分區(qū)重分配,那么對(duì)于同一個(gè)分區(qū)而言有可能之前的消費(fèi)者和新指派的消費(fèi)者不是同一個(gè),對(duì)于之前消費(fèi)者進(jìn)行到一半的處理還要在新指派的消費(fèi)者中再次復(fù)現(xiàn)一遍,這顯然很浪費(fèi)系統(tǒng)資源。StickyAssignor策略如同其名稱中的“sticky”一樣,讓分配策略具備一定的“粘性”,盡可能地讓前后兩次分配相同,進(jìn)而減少系統(tǒng)資源的損耗以及其它異常情況的發(fā)生。
到目前為止所分析的都是消費(fèi)者的訂閱信息都是相同的情況,我們來(lái)看一下訂閱信息不同的情況下的處理。
舉例: 同樣消費(fèi)組內(nèi)有3個(gè)消費(fèi)者:C0、C1、C2
集群中有3個(gè)主題 t0、t1、t2
這3個(gè)主題分別有 1、2、3個(gè)分區(qū)
也就是說(shuō)集群中有 t0p0、t1p0、t1p1、t2p0、t2p1、t2p2 這6個(gè)分區(qū)
消費(fèi)者C0訂閱了主題t0,消費(fèi)者C1訂閱了主題t0和t1,消費(fèi)者C2訂閱了主題t0、t1和t2
如果此時(shí)采用RoundRobinAssignor策略:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`消費(fèi)者C0:t0p0
消費(fèi)者C1:t1p0
消費(fèi)者C2:t1p1、t2p0、t2p1、t2p2` </pre>
如果此時(shí)采用的是StickyAssignor策略:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`消費(fèi)者C0:t0p0
消費(fèi)者C1:t1p0、t1p1
消費(fèi)者C2:t2p0、t2p1、t2p2` </pre>
此時(shí)消費(fèi)者C0脫離了消費(fèi)組,那么RoundRobinAssignor策略的分配結(jié)果為:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`消費(fèi)者C1:t0p0、t1p1
消費(fèi)者C2:t1p0、t2p0、t2p1、t2p2` </pre>
StickyAssignor策略,那么分配結(jié)果為:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`消費(fèi)者C1:t1p0、t1p1、t0p0
消費(fèi)者C2:t2p0、t2p1、t2p2` </pre>
可以看到StickyAssignor策略保留了消費(fèi)者C1和C2中原有的5個(gè)分區(qū)的分配:
<pre class="custom" data-tool="mdnice編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">t1p0、t1p1、t2p0、t2p1、t2p2。 </pre>
從結(jié)果上看StickyAssignor策略比另外兩者分配策略而言顯得更加的優(yōu)異,這個(gè)策略的代碼實(shí)現(xiàn)也是異常復(fù)雜。
4.自定義分區(qū)分配策略
可以通過(guò)實(shí)現(xiàn) org.apache.kafka.clients.consumer.internals.PartitionAssignor 接口來(lái)實(shí)現(xiàn)
18.kafka 控制器是什么?有什么作用
在 Kafka 集群中會(huì)有一個(gè)或多個(gè) broker,其中有一個(gè) broker 會(huì)被選舉為控制器,它負(fù)責(zé)管理整個(gè)集群中所有分區(qū)和副本的狀態(tài),kafka 集群中只能有一個(gè)控制器。
- 當(dāng)某個(gè)分區(qū)的 leader 副本出現(xiàn)故障時(shí),由控制器負(fù)責(zé)為該分區(qū)選舉新的 leader 副本。
- 當(dāng)檢測(cè)到某個(gè)分區(qū)的ISR集合發(fā)生變化時(shí),由控制器負(fù)責(zé)通知所有 broker 更新其元數(shù)據(jù)信息。
- 當(dāng)為某個(gè) topic 增加分區(qū)數(shù)量時(shí),由控制器負(fù)責(zé)分區(qū)的重新分配。
19.kafka 控制器是怎么進(jìn)行選舉的?
kafka 中的控制器選舉工作依賴于 Zookeeper,成功競(jìng)選成為控制器的 broker 會(huì)在Zookeeper中創(chuàng)建/controller臨時(shí)節(jié)點(diǎn)。
每個(gè) broker 啟動(dòng)的時(shí)候會(huì)去嘗試讀取/controller 節(jié)點(diǎn)的 brokerid的值
- 如果讀取到的 brokerid 的值不為-1,表示已經(jīng)有其他broker 節(jié)點(diǎn)成功競(jìng)選為控制器,所以當(dāng)前 broker 就會(huì)放棄競(jìng)選;
如果Zookeeper中不存在/controller 節(jié)點(diǎn),或者這個(gè)節(jié)點(diǎn)的數(shù)據(jù)異常,那么就會(huì)嘗試去創(chuàng)建/controller 節(jié)點(diǎn),創(chuàng)建成功的那個(gè) broker 就會(huì)成為控制器。
每個(gè) broker 都會(huì)在內(nèi)存中保存當(dāng)前控制器的 brokerid 值,這個(gè)值可以標(biāo)識(shí)為 activeControllerId。
Zookeeper 中還有一個(gè)與控制器有關(guān)的/controller_epoch 節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)是持久節(jié)點(diǎn),節(jié)點(diǎn)中存放的是一個(gè)整型的 controller_epoch 值。controller_epoch 值用于記錄控制器發(fā)生變更的次數(shù)。
controller_epoch 的初始值為1,即集群中的第一個(gè)控制器的紀(jì)元為1,當(dāng)控制器發(fā)生變更時(shí),每選出一個(gè)新的控制器就將該字段值加1。
每個(gè)和控制器交互的請(qǐng)求都會(huì)攜帶 controller_epoch 這個(gè)字段,
- 如果請(qǐng)求的 controller_epoch 值小于內(nèi)存中的 controller_epoch值,則認(rèn)為這個(gè)請(qǐng)求是向已經(jīng)過(guò)期的控制器發(fā)送的請(qǐng)求,那么這個(gè)請(qǐng)求會(huì)被認(rèn)定為無(wú)效的請(qǐng)求。
- 如果請(qǐng)求的 controller_epoch 值大于內(nèi)存中的 controller_epoch值,那么說(shuō)明已經(jīng)有新的控制器當(dāng)選了
20.kafka 為什么這么快?

-
1.順序讀寫
磁盤分為順序讀寫與隨機(jī)讀寫,基于磁盤的隨機(jī)讀寫確實(shí)很慢,但磁盤的順序讀寫性能卻很高,kafka 這里采用的就是順序讀寫。
-
2.Page Cache
為了優(yōu)化讀寫性能,Kafka 利用了操作系統(tǒng)本身的 Page Cache,就是利用操作系統(tǒng)自身的內(nèi)存而不是JVM空間內(nèi)存。
-
3.零拷貝
Kafka使用了零拷貝技術(shù),也就是直接將數(shù)據(jù)從內(nèi)核空間的讀緩沖區(qū)直接拷貝到內(nèi)核空間的 socket 緩沖區(qū),然后再寫入到 NIC 緩沖區(qū),避免了在內(nèi)核空間和用戶空間之間穿梭。
-
4.分區(qū)分段+索引
Kafka 的 message 是按 topic分 類存儲(chǔ)的,topic 中的數(shù)據(jù)又是按照一個(gè)一個(gè)的 partition 即分區(qū)存儲(chǔ)到不同 broker 節(jié)點(diǎn)。每個(gè) partition 對(duì)應(yīng)了操作系統(tǒng)上的一個(gè)文件夾,partition 實(shí)際上又是按照segment分段存儲(chǔ)的。
通過(guò)這種分區(qū)分段的設(shè)計(jì),Kafka 的 message 消息實(shí)際上是分布式存儲(chǔ)在一個(gè)一個(gè)小的 segment 中的,每次文件操作也是直接操作的 segment。為了進(jìn)一步的查詢優(yōu)化,Kafka 又默認(rèn)為分段后的數(shù)據(jù)文件建立了索引文件,就是文件系統(tǒng)上的.index文件。這種分區(qū)分段+索引的設(shè)計(jì),不僅提升了數(shù)據(jù)讀取的效率,同時(shí)也提高了數(shù)據(jù)操作的并行度。
-
5.批量讀寫
Kafka 數(shù)據(jù)讀寫也是批量的而不是單條的,這樣可以避免在網(wǎng)絡(luò)上頻繁傳輸單個(gè)消息帶來(lái)的延遲和帶寬開銷。假設(shè)網(wǎng)絡(luò)帶寬為10MB/S,一次性傳輸10MB的消息比傳輸1KB的消息10000萬(wàn)次顯然要快得多。
-
6.批量壓縮
Kafka 把所有的消息都變成一個(gè)批量的文件,并且進(jìn)行合理的批量壓縮,減少網(wǎng)絡(luò) IO 損耗,通過(guò) mmap 提高 I/O 速度,寫入數(shù)據(jù)的時(shí)候由于單個(gè)Partion是末尾添加所以速度最優(yōu);讀取數(shù)據(jù)的時(shí)候配合 sendfile 進(jìn)行直接讀取。
21.什么情況下 kafka 會(huì)丟失消息?
Kafka 有三次消息傳遞的過(guò)程: 生產(chǎn)者發(fā)消息給 Broker,Broker 同步消息和持久化消息,Broker 將消息傳遞給消費(fèi)者。

這其中每一步都有可能丟失消息.
-
1.生產(chǎn)者發(fā)送數(shù)據(jù): 在第 11 問(wèn)中的 acks中有說(shuō)到
- 當(dāng) acks 為 0,只要服務(wù)端寫消息時(shí)出現(xiàn)任何問(wèn)題,都會(huì)導(dǎo)致消息丟失。
- 當(dāng) acks 配置為 1 時(shí),生產(chǎn)者發(fā)送消息,只要 leader 副本成功寫入消息,就代表成功。這種方案的問(wèn)題在于,當(dāng)返回成功后,如果 leader 副本和 follower 副本還沒(méi)有來(lái)得及同步,leader 就崩潰了,那么在選舉后新的 leader 就沒(méi)有這條消息,也就丟失了。
-
2.Broker 存儲(chǔ)數(shù)據(jù):kafka 通過(guò) Page Cache 將數(shù)據(jù)寫入磁盤。
- Page Cache 就是當(dāng)往磁盤文件寫入的時(shí)候,系統(tǒng)會(huì)先將數(shù)據(jù)流寫入緩存中,但是什么時(shí)候?qū)⒕彺娴臄?shù)據(jù)寫入文件中是由操作系統(tǒng)自行決定。所以如果此時(shí)機(jī)器突然掛了,也是會(huì)丟失消息的。
3.消費(fèi)者消費(fèi)數(shù)據(jù):在開啟自動(dòng)提交 offset 時(shí),只要消費(fèi)者消費(fèi)到消息,那么就會(huì)自動(dòng)提交偏移量,如果業(yè)務(wù)還沒(méi)有來(lái)得及處理,那么消息就會(huì)丟失。