《我想進大廠》之kafka奪命連環11問

干啥啥不行,看小說第一名。這不,好好寫了一篇文章。

最近整理了一下文章目錄,因為好早之前就有兄弟跟我說之前文章找不到,我也懶得整理,現在好好整了一下,發現有一篇文章寫了一半我就放著了,抽空把他剛好補齊了一下,之前放著沒寫大概是很難想到從哪里湊這么多問題???

看這里,找文章:文章目錄

說說你對kafka的理解

kafka是一個流式數據處理平臺,他具有消息系統的能力,也有實時流式數據處理分析能力,只是我們更多的偏向于把他當做消息隊列系統來使用。

如果說按照容易理解來分層的話,大致可以分為3層:

第一層是Zookeeper,相當于注冊中心,他負責kafka集群元數據的管理,以及集群的協調工作,在每個kafka服務器啟動的時候去連接到Zookeeper,把自己注冊到Zookeeper當中

第二層里是kafka的核心層,這里就會包含很多kafka的基本概念在內:

record:代表消息

topic:主題,消息都會由一個主題方式來組織,可以理解為對于消息的一個分類

producer:生產者,負責發送消息

consumer:消費者,負責消費消息

broker:kafka服務器

partition:分區,主題會由多個分區組成,通常每個分區的消息都是按照順序讀取的,不同的分區無法保證順序性,分區也就是我們常說的數據分片sharding機制,主要目的就是為了提高系統的伸縮能力,通過分區,消息的讀寫可以負載均衡到多個不同的節點上

Leader/Follower:分區的副本。為了保證高可用,分區都會有一些副本,每個分區都會有一個Leader主副本負責讀寫數據,Follower從副本只負責和Leader副本保持數據同步,不對外提供任何服務

offset:偏移量,分區中的每一條消息都會根據時間先后順序有一個遞增的序號,這個序號就是offset偏移量

Consumer group:消費者組,由多個消費者組成,一個組內只會由一個消費者去消費一個分區的消息

Coordinator:協調者,主要是為消費者組分配分區以及重平衡Rebalance操作

Controller:控制器,其實就是一個broker而已,用于協調和管理整個Kafka集群,他會負責分區Leader選舉、主題管理等工作,在Zookeeper第一個創建臨時節點/controller的就會成為控制器

第三層則是存儲層,用來保存kafka的核心數據,他們都會以日志的形式最終寫入磁盤中。

image

消息隊列模型知道嗎?kafka是怎么做到支持這兩種模型的?

對于傳統的消息隊列系統支持兩個模型:

  1. 點對點:也就是消息只能被一個消費者消費,消費完后消息刪除
  2. 發布訂閱:相當于廣播模式,消息可以被所有消費者消費

上面也說到過,kafka其實就是通過Consumer Group同時支持了這兩個模型。

如果說所有消費者都屬于一個Group,消息只能被同一個Group內的一個消費者消費,那就是點對點模式。

如果每個消費者都是一個單獨的Group,那么就是發布訂閱模式。

實際上,Kafka通過消費者分組的方式靈活的支持了這兩個模型。

能說說kafka通信過程原理嗎?

  1. 首先kafka broker啟動的時候,會去向Zookeeper注冊自己的ID(創建臨時節點),這個ID可以配置也可以自動生成,同時會去訂閱Zookeeper的brokers/ids路徑,當有新的broker加入或者退出時,可以得到當前所有broker信息
  2. 生產者啟動的時候會指定bootstrap.servers,通過指定的broker地址,Kafka就會和這些broker創建TCP連接(通常我們不用配置所有的broker服務器地址,否則kafka會和配置的所有broker都建立TCP連接)
  3. 隨便連接到任何一臺broker之后,然后再發送請求獲取元數據信息(包含有哪些主題、主題都有哪些分區、分區有哪些副本,分區的Leader副本等信息)
  4. 接著就會創建和所有broker的TCP連接
  5. 之后就是發送消息的過程
  6. 消費者和生產者一樣,也會指定bootstrap.servers屬性,然后選擇一臺broker創建TCP連接,發送請求找到協調者所在的broker
  7. 然后再和協調者broker創建TCP連接,獲取元數據
  8. 根據分區Leader節點所在的broker節點,和這些broker分別創建連接
  9. 最后開始消費消息
image

那么發送消息時如何選擇分區的?

主要有兩種方式:

  1. 輪詢,按照順序消息依次發送到不同的分區
  2. 隨機,隨機發送到某個分區

如果消息指定key,那么會根據消息的key進行hash,然后對partition分區數量取模,決定落在哪個分區上,所以,對于相同key的消息來說,總是會發送到同一個分區上,也是我們常說的消息分區有序性。

很常見的場景就是我們希望下單、支付消息有順序,這樣以訂單ID作為key發送消息就達到了分區有序性的目的。

如果沒有指定key,會執行默認的輪詢負載均衡策略,比如第一條消息落在P0,第二條消息落在P1,然后第三條又在P1。

除此之外,對于一些特定的業務場景和需求,還可以通過實現Partitioner接口,重寫configurepartition方法來達到自定義分區的效果。

好,那你覺得為什么需要分區?有什么好處?

這個問題很簡單,如果說不分區的話,我們發消息寫數據都只能保存到一個節點上,這樣的話就算這個服務器節點性能再好最終也支撐不住。

實際上分布式系統都面臨這個問題,要么收到消息之后進行數據切分,要么提前切分,kafka正是選擇了前者,通過分區可以把數據均勻地分布到不同的節點。

分區帶來了負載均衡和橫向擴展的能力。

發送消息時可以根據分區的數量落在不同的Kafka服務器節點上,提升了并發寫消息的性能,消費消息的時候又和消費者綁定了關系,可以從不同節點的不同分區消費消息,提高了讀消息的能力。

另外一個就是分區又引入了副本,冗余的副本保證了Kafka的高可用和高持久性。

詳細說說消費者組和消費者重平衡?

Kafka中的消費者組訂閱topic主題的消息,一般來說消費者的數量最好要和所有主題分區的數量保持一致最好(舉例子用一個主題,實際上當然是可以訂閱多個主題)。

當消費者數量小于分區數量的時候,那么必然會有一個消費者消費多個分區的消息。

而消費者數量超過分區的數量的時候,那么必然會有消費者沒有分區可以消費。

所以,消費者組的好處一方面在上面說到過,可以支持多種消息模型,另外的話根據消費者和分區的消費關系,支撐橫向擴容伸縮。

image

當我們知道消費者如何消費分區的時候,就顯然會有一個問題出現了,消費者消費的分區是怎么分配的,有先加入的消費者時候怎么辦?

舊版本的重平衡過程主要通過ZK監聽器的方式來觸發,每個消費者客戶端自己去執行分區分配算法。

新版本則是通過協調者來完成,每一次新的消費者加入都會發送請求給協調者去獲取分區的分配,這個分區分配的算法邏輯由協調者來完成。

而重平衡Rebalance就是指的有新消費者加入的情況,比如剛開始我們只有消費者A在消費消息,過了一段時間消費者B和C加入了,這時候分區就需要重新分配,這就是重平衡,也可以叫做再平衡,但是重平衡的過程和我們的GC時候STW很像,會導致整個消費群組停止工作,重平衡期間都無法消息消息。

另外,發生重平衡并不是只有這一種情況,因為消費者和分區總數是存在綁定關系的,上面也說了,消費者數量最好和所有主題的分區總數一樣。

那只要消費者數量主題數量(比如用的正則訂閱的主題)、分區數量任何一個發生改變,都會觸發重平衡。

下面說說重平衡的過程。

重平衡的機制依賴消費者和協調者之間的心跳來維持,消費者會有一個獨立的線程去定時發送心跳給協調者,這個可以通過參數heartbeat.interval.ms來控制發送心跳的間隔時間。

  1. 每個消費者第一次加入組的時候都會向協調者發送JoinGroup請求,第一個發送這個請求的消費者會成為“群主”,協調者會返回組成員列表給群主

  2. 群主執行分區分配策略,然后把分配結果通過SyncGroup請求發送給協調者,協調者收到分區分配結果

  3. 其他組內成員也向協調者發送SyncGroup,協調者把每個消費者的分區分配分別響應給他們

image

那你跟我再具體講講分區分配策略?

主要有3種分配策略:

Range

不知道咋翻譯,這個是默認的策略。大概意思就是對分區進行排序,排序越靠前的分區能夠分配到更多的分區。

比如有3個分區,消費者A排序更靠前,所以能夠分配到P0\P1兩個分區,消費者B就只能分配到一個P2。

如果是4個分區的話,那么他們會剛好都是分配到2個。

image

但是這個分配策略會有點小問題,他是根據主題進行分配,所以如果消費者組訂閱了多個主題,那就有可能導致分區分配不均衡。

比如下圖中兩個主題的P0\P1都被分配給了A,這樣A有4個分區,而B只有2個,如果這樣的主題數量越多,那么不均衡就越嚴重。

image

RoundRobin

也就是我們常說的輪詢了,這個就比較簡單了,不畫圖你也能很容易理解。

這個會根據所有的主題進行輪詢分配,不會出現Range那種主題越多可能導致分區分配不均衡的問題。

P0->A,P1->B,P1->A。。。以此類推

image

Sticky

這個從字面看來意思就是粘性策略,大概是這個意思。主要考慮的是在分配均衡的前提下,讓分區的分配更小的改動。

比如之前P0\P1分配給消費者A,那么下一次盡量還是分配給A。

這樣的好處就是連接可以復用,要消費消息總是要和broker去連接的,如果能夠保持上一次分配的分區的話,那么就不用頻繁的銷毀創建連接了。

來吧!如何保證消息可靠性?

消息可靠性的保證基本上我們都要從3個方面來闡述(這樣才比較全面,無懈可擊)

生產者發送消息丟失

kafka支持3種方式發送消息,這也是常規的3種方式,發送后不管結果、同步發送、異步發送,基本上所有的消息隊列都是這樣玩的。

  1. 發送并忘記,直接調用發送send方法,不管結果,雖然可以開啟自動重試,但是肯定會有消息丟失的可能
  2. 同步發送,同步發送返回Future對象,我們可以知道發送結果,然后進行處理
  3. 異步發送,發送消息,同時指定一個回調函數,根據結果進行相應的處理

為了保險起見,一般我們都會使用異步發送帶有回調的方式進行發送消息,再設置參數為發送消息失敗不停地重試。

acks=all,這個參數有可以配置0|1|all。

0表示生產者寫入消息不管服務器的響應,可能消息還在網絡緩沖區,服務器根本沒有收到消息,當然會丟失消息。

1表示至少有一個副本收到消息才認為成功,一個副本那肯定就是集群的Leader副本了,但是如果剛好Leader副本所在的節點掛了,Follower沒有同步這條消息,消息仍然丟失了。

配置all的話表示所有ISR都寫入成功才算成功,那除非所有ISR里的副本全掛了,消息才會丟失。

retries=N,設置一個非常大的值,可以讓生產者發送消息失敗后不停重試

kafka自身消息丟失

kafka因為消息寫入是通過PageCache異步寫入磁盤的,因此仍然存在丟失消息的可能。

因此針對kafka自身丟失的可能設置參數:

replication.factor=N,設置一個比較大的值,保證至少有2個或者以上的副本。

min.insync.replicas=N,代表消息如何才能被認為是寫入成功,設置大于1的數,保證至少寫入1個或者以上的副本才算寫入消息成功。

unclean.leader.election.enable=false,這個設置意味著沒有完全同步的分區副本不能成為Leader副本,如果是true的話,那些沒有完全同步Leader的副本成為Leader之后,就會有消息丟失的風險。

消費者消息丟失

消費者丟失的可能就比較簡單,關閉自動提交位移即可,改為業務處理成功手動提交。

因為重平衡發生的時候,消費者會去讀取上一次提交的偏移量,自動提交默認是每5秒一次,這會導致重復消費或者丟失消息。

enable.auto.commit=false,設置為手動提交。

還有一個參數我們可能也需要考慮進去的:

auto.offset.reset=earliest,這個參數代表沒有偏移量可以提交或者broker上不存在偏移量的時候,消費者如何處理。earliest代表從分區的開始位置讀取,可能會重復讀取消息,但是不會丟失,消費方一般我們肯定要自己保證冪等,另外一種latest表示從分區末尾讀取,那就會有概率丟失消息。

綜合這幾個參數設置,我們就能保證消息不會丟失,保證了可靠性。

OK,聊聊副本和它的同步原理吧?

Kafka副本的之前提到過,分為Leader副本和Follower副本,也就是主副本和從副本,和其他的比如Mysql不一樣的是,Kafka中只有Leader副本會對外提供服務,Follower副本只是單純地和Leader保持數據同步,作為數據冗余容災的作用。

在Kafka中我們把所有副本的集合統稱為AR(Assigned Replicas),和Leader副本保持同步的副本集合稱為ISR(InSyncReplicas)

ISR是一個動態的集合,維持這個集合會通過replica.lag.time.max.ms參數來控制,這個代表落后Leader副本的最長時間,默認值10秒,所以只要Follower副本沒有落后Leader副本超過10秒以上,就可以認為是和Leader同步的(簡單可以認為就是同步時間差)。

另外還有兩個關鍵的概念用于副本之間的同步:

HW(High Watermark):高水位,也叫做復制點,表示副本間同步的位置。如下圖所示,04綠色表示已經提交的消息,這些消息已經在副本之間進行同步,消費者可以看見這些消息并且進行消費,46黃色的則是表示未提交的消息,可能還沒有在副本間同步,這些消息對于消費者是不可見的。

LEO(Log End Offset):下一條待寫入消息的位移

hw

<figcaption style="margin-top: 5px; text-align: center; color: #888; font-size: 12px;">hw</figcaption>

副本間同步的過程依賴的就是HW和LEO的更新,以他們的值變化來演示副本同步消息的過程,綠色表示Leader副本,黃色表示Follower副本。

首先,生產者不停地向Leader寫入數據,這時候Leader的LEO可能已經達到了10,但是HW依然是0,兩個Follower向Leader請求同步數據,他們的值都是0。

image

然后,消息還在繼續寫入,Leader的LEO值又發生了變化,兩個Follower也各自拉取到了自己的消息,于是更新自己的LEO值,但是這時候Leader的HW依然沒有改變。

image

此時,Follower再次向Leader拉取數據,這時候Leader會更新自己的HW值,取Follower中的最小的LEO值來更新。

image

之后,Leader響應自己的HW給Follower,Follower更新自己的HW值,因為又拉取到了消息,所以再次更新LEO,流程以此類推。

image

你知道新版本Kafka為什么拋棄了Zookeeper嗎?

我認為可以從兩個個方面來回答這個問題:

首先,從運維的復雜度來看,Kafka本身是一個分布式系統,他的運維就已經很復雜了,那除此之外,還需要重度依賴另外一個ZK,這對成本和復雜度來說都是一個很大的工作量。

其次,應該是考慮到性能方面的問題,比如之前的提交位移的操作都是保存在ZK里面的,但是ZK實際上不適合這種高頻的讀寫更新操作,這樣的話會嚴重影響ZK集群的性能,這一方面后來新版本中Kafka也把提交和保存位移用消息的方式來處理了。

另外Kafka嚴重依賴ZK來實現元數據的管理和集群的協調工作,如果集群規模龐大,主題和分區數量很多,會導致ZK集群的元數據過多,集群壓力過大,直接影響到很多Watch的延時或者丟失。

OK,最后一個大家都問的問題,Kafka為什么快?

嘿,這個我費,我背過好多次了!主要是3個方面:

順序IO

kafka寫消息到分區采用追加的方式,也就是順序寫入磁盤,不是隨機寫入,這個速度比普通的隨機IO快非常多,幾乎可以和網絡IO的速度相媲美。

Page Cache和零拷貝

kafka在寫入消息數據的時候通過mmap內存映射的方式,不是真正立刻寫入磁盤,而是利用操作系統的文件緩存PageCache異步寫入,提高了寫入消息的性能,另外在消費消息的時候又通過sendfile實現了零拷貝。

關于mmap和sendfile零拷貝我都專門寫過,可以看這里:阿里二面:什么是mmap?

批量處理和壓縮

Kafka在發送消息的時候不是一條條的發送的,而是會把多條消息合并成一個批次進行處理發送,消費消息也是一個道理,一次拉取一批次的消息進行消費。

并且Producer、Broker、Consumer都使用了優化后的壓縮算法,發送和消息消息使用壓縮節省了網絡傳輸的開銷,Broker存儲使用壓縮則降低了磁盤存儲的空間。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容