kafka的一些理解和麪試題2

關鍵概念介紹

topic

以下是kafka的邏輯結構圖: 每個topic也就是自定義的一個隊列,producer往隊列中放消息,consumer從隊列中取消息,topic之間相互獨立。
在這裏插入圖片描述
broker

與上圖對應的是kafka的物理結構圖:每個broker通常就是一臺物理機器,在上面運行kafka server的一個實例,所有這些broker實例組成kafka的服務器集羣。

每個broker會給自己分配一個唯一的broker id。broker集羣是通過zookeeper集羣來管理的。每個broker都會註冊到zookeeper上,有某個機器掛了,有新的機器加入,zookeeper都會收到通知。

在0.9.0中,producer/consumer已經不會依賴Zookeeper來獲取集羣的配置信息,而是通過任意一個broker來獲取整個集羣的配置信息。如下圖所示:只有服務端依賴zk,客戶端不依賴zk。
在這裏插入圖片描述
partition

kafka的topic,在每個機器上,是用文件存儲的。而這些文件呢,會分目錄。partition就是文件的目錄。比如一個topic叫abc,分了10個partion,則在機器的目錄上,就是:
abc_0
abc_1
abc_2
abc_3

abc_9

然後每個目錄裏面,存放了一堆消息文件,消息是順序append log方式存儲的。關於這個,後面會詳細闡述。

replica/leader/follower

每個topic的partion的所有消息,都不是隻存1份,而是在多個broker上冗餘存儲,從而提高系統的可靠性。這多臺機器就叫一個replica集合。

在這個replica集合中,需要選出1個leader,剩下的是follower。也就是master/slave。

發送消息的時候,只會發送給leader,然後leader再把消息同步給followers(以pull的方式,followers去leader上pull,而不是leader push給followers)。

那這裏面就有一個問題:leader收到消息之後,是直接返回給producer呢,還是等所有followers都寫完消息之後,再返回? 關於這個,後面會相信闡述。

關鍵點:這裏replica/leader/follower都是邏輯概念,並且是相對”partion”來講的,而不是”topic”。也就說,同一個topic的不同partion,對於的replica集合可以是不一樣的。

比如
“abc-0” <1,3,5> //abc_0的replica集合是borker 1, 3, 5, leader是1, follower是3, 5
“abc-1” <1,3,7> //abc_1的replica集合是broker 1, 3, 7,leader是1, follower是3, 7
“abc_2” < 3,7,9>
“abc_3” <1,7,9>
“abc_4” <1,3,5>



消息隊列的各種策略和語義

對於消息隊列的使用,表面上看起來很簡單,一端往裏面放,一端從裏面取。但就在這一放一取中,存在着諸多策略。

Producer的策略

是否ACK

所謂ACK,是指服務器收到消息之後,是存下來之後,再給客戶端返回,還是直接返回。很顯然,是否ACK,是影響性能的一個重要指標。在kafka中,request.required.acks有3個取值,分別對應3種策略:

request.required.acks

//0: 不等服務器ack就返回了,性能最高,可能丟數據
//1. leader確認消息存下來了,再返回
//all: leader和當前ISR中所有replica都確認消息存下來了,再返回(這種方式最可靠)

備註:在0.9.0以前的版本,是用-1表示all

同步發送 vs 異步發送

所謂異步發送,就是指客戶端有個本地緩衝區,消息先存放到本地緩衝區,然後有後臺線程來發送。

在0.8.2和0.8.2之前的版本中,同步發送和異步發送是分開實現的,用的Scala語言。從0.8.2開始,引入了1套新的Java版的client api。在0.9及其以後中,同步實際上是用異步間接實現的:

Consumer的策略

Push vs Pull

所有的消息隊列都要面對一個問題,是broker把消息Push給消費者呢,還是消費者主動去broker Pull消息?

kafka選擇了pull的方式,爲什麼呢? 因爲pull的方式更靈活:消息發送頻率應該如何,消息是否可以延遲然後batch發送,這些信息只有消費者自己最清楚!

因此把控制權交給消費者,消費者自己控制消費的速率,當消費者處理消息很慢時,它可以選擇減緩消費速率;當處理消息很快時,它可以選擇加快消費速率。而在push的方式下,要實現這種靈活的控制策略,就需要額外的協議,讓消費者告訴broker,要減緩還是加快消費速率,這增加了實現的複雜性。

另外pull的方式下,消費者可以很容易的自適應控制消息是batch的發送,還是最低限度的減少延遲,每來1個就發送1個。

消費的confirm

在消費端,所有消息隊列都要解決的一個問題就是“消費確認問題”:消費者拿到一個消息,然後處理這個消息的時候掛了,如果這個時候broker認爲這個消息已經消費了,那這條消息就丟失了。

一個解決辦法就是,消費者在消費完之後,再往broker發個confirm消息。broker收到confirm消息之後,再把消息刪除。

要實現這個,broker就要維護每個消息的狀態,已發送/已消費,很顯然,這會增大broker的實現難度。同時,這還有另外一個問題,就是消費者消費完消息,發送confirm的時候,掛了。這個時候會出現重複消費的問題。

kafka沒有直接解決這個問題,而是引入offset回退機制,變相解決了這個問題。在kafka裏面,消息會存放一個星期,纔會被刪除。並且在一個partion裏面,消息是按序號遞增的順序存放的,因此消費者可以回退到某一個歷史的offset,進行重新消費。

當然,對於重複消費的問題,需要消費者去解決。

broker的策略

消息的順序問題

在某些業務場景下,需要消息的順序不能亂:發送順序和消費順序要嚴格一致。而在kafka中,同一個topic,被分成了多個partition,這多個partition之間是互相獨立的。

之所以要分成多個partition,是爲了提高併發度,多個partition並行的進行發送/消費,但這卻沒有辦法保證消息的順序問題。

一個解決辦法是,一個topic只用一個partition,但這樣很顯然限制了靈活性。

還有一個辦法就是,所有發送的消息,用同一個key,這樣同樣的key會落在一個partition裏面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章