Kafka到底有多高可靠?(RNG NB)

在聊Kafka高可靠之前,先在評論區來波RNG NB好不好!

什麼叫可靠性?

大家都知道,系統架構有三高:「高性能、高併發和高可用」,三者的重要性不言而喻。

對於任意系統,想要同時滿足三高都是一件非常困難的事情,大型業務系統或者傳統中間件都會搭建複雜的架構來保證。

除以上三種模式之外,還有一個指標方向也很重要,那就是高可靠,甚至你可能會將它和「高可用」混淆起來。

事實上兩者並不一樣,高可用會更偏向於整體服務的可用性,防止系統宕機等等。而高可靠是指數據的可靠性保證嘛,你可以理解”高可靠“相比於系統三高會是一個更細一點的概念。

那麼什麼是數據的高可靠呢,總結一下就是系統要提供可靠的數據支撐,不能發生丟失、重複等錯誤現象。

所以每個開源中間件在發佈版本時都會通過文檔聲明自己是超可靠的,就像520那天每一位暖男說的那樣。

咱今天的主角kafka就是這麼一個例子。

一些重要概念

因爲有一段時間沒講消息隊列了嘛,爲了幫助你更好理解文章,我們來先複習一下kafka的基礎概念:

  • record:消息,消息隊列基礎通信單位

  • topic:主題,目的就是將消息進行分類,不同業務類型的消息通常會被分發到不同的主題

  • partition:分區,每個主題可以創建多個分區,每個分區都由一系列有序和不可變的消息組成

  • replica:副本,每個分區都有一個至多個副本存在,它的主要作用是存儲保存數據,以日誌(Log)對象的形式體現。副本又分爲leader副本和follower副本

  • offset:偏移量,每一個消息在日誌文件中的位置都對應一個按序遞增的偏移量,你可以理解爲類似數組的存儲形式

  • producer:生產者,生產消息的那一方

  • consumer:消費者,通常不同的業務都會有一到多個消費者組成消費者集羣

  • broker:代理,一個Kafka集羣由一個或多個Kafka實例構成,每一個Kafka實例就稱爲代理

如上圖所示,一共存在主題1和主題2,主題1有兩個分區,主題2只有一個分區,並且每個分區都存在一個leader副本和兩個follower副本,它們分佈在每個不同的代理節點上

partition裏只有leader副本負責與生產者、消費者之間數據的交互,follower副本會定期從leader副本拉取數據以保證整個集羣數據可用性。

如何保證數據高可靠

Kafka是通過副本機制實現數據的存儲的,所以就需要一些機制保證數據在跨集羣的副本之間能夠可靠地傳輸。

1.副本同步集合

業務數據封裝成消息在系統中流轉,由於各個組件都是分佈在不同的服務器上的,所以主題和生產者、消費者之間的數據同步可能存在一定的時間延遲,Kafka通過延遲範圍劃分了幾個不同的集合:

AR(Assigned Replicas)

指的是已經分配數據的分區副本,通常指的是leader副本 + follower副本。

ISR(In Sync Replicas)

指的是和leader副本數據保持同步的副本集合。當follower副本數據和leader副本數據保持同步,那麼這些副本就處在ISR裏面,ISR集合會根據數據的同步狀態動態變化。

OSR(Out Sync Replicas)

一旦follower副本的數據同步進度跟不上leader了,那麼它就會被放進叫做OSR的集合裏。也就是這個集合包含的是不處於同步狀態的分區副本。

OK,那有什麼標準判斷它是同步還是不同步呢?

通過replica.lag.time.max.ms這個參數來設置數據同步時間差,它的默認值是10s。

一旦從分區副本和主分區副本的消息相差10s以上,那麼就認爲消息處於OSR不同步的狀態。若follower處於OSR集合裏,那麼在選取新的leader的時候就不會選舉它作爲新leader。

2.ACK應答機制

我們剛剛說了kafka是通過ack來發送數據同步信號的,那信號發送頻率又有幾種設定呢?

  • ack = 0

生產者發送一次消息就不再發送。不管是否發送成功,若發出去的消息處於通信的路上就丟失,或者還未做磁盤持久化操作,那麼消息就可能丟失。

它的好處就是性能很高,你想呀你發送消息都不需要等待對方回覆就持續發送下一批,那麼消息等待的時間就節省出來了。同一時間範圍內能比別人處理更多數據,缺點就是它的可靠性真的很低,數據真的是說丟就丟。

  • ack = 1

leader接收到消息並且寫入到本地磁盤後就認爲消息處理成功。這種方式可靠性會比上一種好一些,當leader接收到消息並且寫入到本地磁盤後就認爲消息處理成功,不論follower是否同步完這條消息就會返回給producer。

但是假如此刻partition leader所在的broker宕機了,如果那麼數據也可能會丟失,所以follower副本的數據同步就很重要。

Kafka默認就採用這種方式。

  • ack = -1

producer只有收到分區內所有副本的響應ACK纔會認爲消息已經push成功。

這種方式雖然對於數據的可靠保障做得很好,但是就是性能很差,影響吞吐量,所以一般也不會採取。

那麼它就絕對可靠嗎?也不一定。最重要的還是取決於副本數據是否同步完成。若producer收到響應消息前leader副本掛掉,那麼producer會因未收到消息重複發送消息,那就可能造成數據重複。怎麼解決呢?只要保證業務冪等就行。

我們可以通過request.required.acks這個參數控制消息的發送頻率。

如果覺得文章不錯,可以微信搜一搜「 敖丙 」第一時間閱讀,關注後回覆【資料】有我準備的一線大廠面試資料和簡歷模板

3.消息語義

消息集羣整體是一個複雜的系統,所以過程中可能會因爲各種原因導致消息傳遞出錯,Kafka對於這些可能遇到的場景定義了對應的的消息語義。

at most once

它代表消息可能被消費者消費0次或者1次。若場景如下:

  • 消息從partition分發給消費者集羣

  • 消費者把自己收到的消息告訴集羣,集羣收到之後offset就會往後移動

  • 消費者將數據入庫做持久化

你一定想到了。在第三步消費者將消息入庫時若因任何原因消費者A掛了,那麼在將消費者切換到集羣的消費者B後,數據還沒入庫呢。此時partition是渾然不知的呀,那麼這就會造成一個問題:數據丟失。

at least once

它代表partition分發的消息至少被消費一次。其通信過程如下:

  • 消息從partition分發給消費者集羣

  • 消費者將數據入庫做持久化

  • 消費者把自己收到的消息告訴集羣,集羣收到之後offset就會往後移動

假設consumer group在數據入庫之後,在將數據返回給partition的過程中消費者A掛了,那麼partition會因爲接收不到響應ACK而重新發送數據,此時消費者B可能再次將原先的消息入庫,這就造成了數據重複了。

在沒有做任何冪等性保護的情況下,像重複轉賬,重付疊加積分這種業務,那麼結果可能是致命的。

exactly once

代表消息正好能被消費一次,不丟失,不重複。

在at least once的情況基礎上,假設consumerA在返回ack給partition的過程中宕機了。那麼consumerB不會跟着partition的offset走,它會先去數據庫裏面查看最新消息對應的偏移位,再根據這個偏移位返回Kafka集羣從對應的偏移位置出發,這就可以避免消息重複和消息丟失。

不知道有多少小夥伴看到這裏的,如果覺得目前爲止寫的還不錯的,可以幫忙點個贊讓,讓我看看有多少好學的寶寶。

4.數據截斷機制

我們開頭說了真正處理數據的是leader副本,follower副本只負責數據的同步和保存,那如果因爲leader宕機了二者數據不一致會怎麼樣呢?

在講一致性保證過程之前還需瞭解兩個Kafka用於表示副本數據同步的概念:

HW(High Watermark):中文翻譯爲高水位,用來體現副本間數據同步的相對位置,consumer最多隻能消費到HW所在的位置,通過HW我們可以判斷數據對副本是否可見。

LEO(Log End Offset):下一條待寫入消息的記錄位置。

leader副本從生產者獲取消息,follower副本實時從leder同步數據,此時它們的同步數據是一致的都同步到2這個位置,並且下一個寫入的消息都是偏移位4:

假設因爲意外leader發生宕機,follower即被選爲新leader,此後從生產者寫入最新的偏移位4和5:

過了一段時間原leader通過修復恢復服務,它就會發現自己和新leader的數據是不一致的:

爲了保證數據一致性就必須強行讓一方妥協。因爲數據是不斷在刷新的,所以舊leader此時的優先級會小於新leader,因此它會將自己的數據截斷到與新leader相同的HW和LEO位置,確保和新leader的數據一定相同,這就是Kafka數據截斷機制。

5.數據清理機制

同其它中間件一樣,Kafka的主要作用是通信,所以即使是將數據保存在磁盤上它還是會佔用一定空間。爲了節約存儲空間它會通過一些機制對過期數據進行清理。

日誌刪除

日誌刪除會直接刪除日誌分段,kafka會維護一個定時任務來週期性檢查和刪除「過期數據」

  • 基於時間的日誌刪除

它在每一個日誌段文件裏面都維護一個最大時間戳來確認當前配置的刪除時間,只要日誌段寫入新消息該字段都會被更新。一個日誌段被寫滿了之後就不會再接收新的消息,它會去創建一個新的日誌段文件往裏面寫數據。

每一個日誌段文件被寫滿之後它的最大的時間戳都是保持不變的,Kafka只要通過當前時間與最大時間戳進行比較就可以判斷該日誌段文件是否過期。

Kafka默認配置log.retention.hours = 168,也就是7天的日誌保留時間。

  • 基於容量大小的日誌刪除

這和以上是異曲同工的方式, 只不過這次從時間換成了空間。

Kafka會通過每個日誌段空間的大小計算一個總容量閾值,然後計算出當前的實際空間大小和總容量閾值的差值,如果這個差值大於單個日誌段文件的大小那麼就會刪除掉最舊的那個日誌段文件,反之則不做任何處理。

同理,這個閾值也可以通過log.retention.bytes參數來設置。

日誌壓縮

Kafka的消息是由鍵值組成的,如果日誌段裏存在多條相同key但是不同value的數據,那麼它會選擇性地清除舊數據,保留最近一條記錄。

具體的壓縮方式就是創建一個檢查點文件,從日誌起始位置開始遍歷到最大結束位置,然後把每個消息的key和key對應的offset保存在一個固定容量的SkimpyOffsetMap中。

這樣前面的值就會被後面的覆蓋掉,如果日誌文件裏存在相同的key只有最新的那個會被保留。

總結

Kafka通過ACK應答機制保證了不同組件之間的通信效率,通過副本同步機制、數據截斷和數據清理機制實現了對於數據的管理策略,保證整個系統運行效率。

作爲一款高性能又同時兼顧高可靠性的消息中間件來說,Kafka能吹的點實在太多。如果本篇文章對你有所幫助,點擊一下右下角的大拇指,下一次我們來詳細講解Kafka是如何實現副本間數據傳遞的。

你知道的越多,不知道的越多,各位的點贊評論都對我很重要,如果這篇文章有幫助你多一點點了解Kafka的話,可以在評論區來一波“變得更強”。

也希望你的bug和下面這張圖一樣,🤺 退 🤺退 🤺退!我們下次見。


文章持續更新,可以微信搜一搜「 敖丙 」第一時間閱讀,關注後回覆【資料】有我準備的一線大廠面試資料和簡歷模板,本文 GitHub https://github.com/JavaFamily 已經收錄,有大廠面試完整考點,歡迎Star。

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