這些年背過的面試題——Kafka篇

大資料技術前線發表於2024-02-20

來源:阿里雲開發者


本文是技術人面試系列Kafka篇,面試中關於Kafka都需要了解哪些基礎?一文帶你詳細瞭解,歡迎收藏!



Why kafka

訊息佇列的作用:非同步、削峰填谷、解耦
中小型公司,技術挑戰不是特別高,用 RabbitMQ (開源、社群活躍)是不錯的選擇;大型公司,基礎架構研發實力較強,用 RocketMQ(Java二次開發) 是很好的選擇。
如果是大資料領域的實時計算、日誌採集等場景,用 Kafka 是業內標準的,絕對沒問題,社群活躍度很高,絕對不會黃,何況幾乎是全世界這個領域的事實性規範。


這些年背過的面試題——Kafka篇


RabbitMQ
RabbitMQ開始是用在電信業務的可靠通訊的,也是少有的幾款支援AMQP協議的產品之一。
優點:


  • 輕量級,快速,部署使用方便

  • 支援靈活的路由配置。RabbitMQ中,在生產者和佇列之間有一個交換器模組。根據配置的路由規則,生產者傳送的訊息可以傳送到不同的佇列中。路由規則很靈活,還可以自己實現。

  • RabbitMQ的客戶端支援大多數的程式語言,支援AMQP協議。

這些年背過的面試題——Kafka篇


缺點:


  • 如果有大量訊息堆積在佇列中,效能會急劇下降

  • 每秒處理幾萬到幾十萬的訊息。如果應用要求高的效能,不要選擇RabbitMQ。 

  • RabbitMQ是Erlang開發的,功能擴充套件和二次開發代價很高。


RocketMQ
借鑑了Kafka的設計並做了很多改進,幾乎具備了訊息佇列應該具備的所有特性和功能。


  • RocketMQ主要用於有序,事務,流計算,訊息推送,日誌流處理,binlog分發等場景。

  • 經過了歷次的雙11考驗,效能,穩定性可靠性沒的說。

  • java開發,閱讀原始碼、擴充套件、二次開發很方便。

  • 對電商領域的響應延遲做了很多最佳化。

  • 每秒處理幾十萬的訊息,同時響應在毫秒級。如果應用很關注響應時間,可以使用RocketMQ。

  • 效能比RabbitMQ高一個數量級。

  • 支援死信佇列,DLX 是一個非常有用的特性。它可以處理異常情況下,訊息不能夠被消費者正確消費而被置入死信佇列中的情況,後續分析程式可以透過消費這個死信佇列中的內容來分析當時所遇到的異常情況,進而可以改善和最佳化系統。


缺點:
跟周邊系統的整合和相容不是很好。
Kafka
高可用,幾乎所有相關的開源軟體都支援,滿足大多數的應用場景,尤其是大資料和流計算領域,


  • Kafka高效,可伸縮,訊息持久化。支援分割槽、副本和容錯。

  • 對批處理和非同步處理做了大量的設計,因此Kafka可以得到非常高的效能。

  • 每秒處理幾十萬非同步訊息訊息,如果開啟了壓縮,最終可以達到每秒處理2000w訊息的級別。

  • 但是由於是非同步的和批處理的,延遲也會高,不適合電商場景。


What Kafka


  • Producer API:允許應用程式將記錄流釋出到一個或多個Kafka主題。

  • Consumer API:允許應用程式訂閱一個或多個主題並處理為其生成的記錄流。

  • Streams API:允許應用程式充當流處理器,將輸入流轉換為輸出流。

這些年背過的面試題——Kafka篇


訊息Message
Kafka的資料單元稱為訊息。可以把訊息看成是資料庫裡的一個“資料行”或一條“記錄”。
批次
為了提高效率,訊息被分批寫入Kafka。提高吞吐量卻加大了響應時間。
主題Topic
透過主題進行分類,類似資料庫中的表。
分割槽Partition
Topic可以被分成若干分割槽分佈於kafka叢集中,方便擴容
單個分割槽內是有序的,partition設定為一才能保證全域性有序
副本Replicas
每個主題被分為若干個分割槽,每個分割槽有多個副本。
生產者Producer
生產者在預設情況下把訊息均衡地分佈到主題的所有分割槽上:


  • 直接指定訊息的分割槽
  • 根據訊息的key雜湊取模得出分割槽
  • 輪詢指定分割槽。


消費者Comsumer
消費者透過偏移量來區分已經讀過的訊息,從而消費訊息。把每個分割槽最後讀取的訊息偏移量儲存在Zookeeper 或Kafka上,如果消費者關閉或重啟,它的讀取狀態不會丟失。
消費組ComsumerGroup
消費組保證每個分割槽只能被一個消費者使用,避免重複消費。如果群組內一個消費者失效,消費組裡的其他消費者可以接管失效消費者的工作再平衡,重新分割槽。
節點Broker
連線生產者和消費者,單個broker可以輕鬆處理數千個分割槽以及每秒百萬級的訊息量。


  • broker接收來自生產者的訊息,為訊息設定偏移量,並提交訊息到磁碟儲存

  • broker為消費者提供服務,響應讀取分割槽的請求,返回已經提交到磁碟上的訊息。


叢集
每隔分割槽都有一個首領,當分割槽被分配給多個broker時,會透過首領進行分割槽複製。 
生產者Offset
訊息寫入的時候,每一個分割槽都有一個offset,即每個分割槽的最新最大的offset。
消費者Offset
不同消費組中的消費者可以針對一個分割槽儲存不同的Offset,互不影響。
LogSegment


  • 一個分割槽由多個LogSegment組成,

  • 一個LogSegment由.log .index .timeindex組成

  • .log追加是順序寫入的,檔名是以檔案中第一條message的offset來命名的

  • .Index進行日誌刪除的時候和資料查詢的時候可以快速定位。

  • .timeStamp則根據時間戳查詢對應的偏移量


How Kafka

優點


  • 高吞吐量:單機每秒處理幾十上百萬的訊息量。即使儲存了TB及訊息,也保持穩定的效能。

    • 零複製 減少核心態到使用者態的複製,磁碟透過sendfile實現DMA 複製Socket buffer

    • 順序讀寫 充分利用磁碟順序讀寫的超高效能

    • 頁快取mmap,將磁碟檔案對映到記憶體, 使用者透過修改記憶體就能修改磁碟檔案。
  • 高效能:單節點支援上千個客戶端,並保證零停機和零資料丟失。

  • 持久化:將訊息持久化到磁碟。透過將資料持久化到硬碟以及replication防止資料丟失。

  • 分散式系統,易擴充套件。所有的元件均為分散式的,無需停機即可擴充套件機器。

  • 可靠性 - Kafka是分散式,分割槽,複製和容錯的。

  • 客戶端狀態維護:訊息被處理的狀態是在Consumer端維護,當失敗時能自動平衡。


應用場景


  • 日誌收集:用Kafka可以收集各種服務的Log,透過大資料平臺進行處理;

  • 訊息系統:解耦生產者和消費者、快取訊息等;

  • 使用者活動跟蹤:Kafka經常被用來記錄Web使用者或者App使用者的各種活動,如瀏覽網頁、搜尋、點選等活動,這些活動資訊被各個伺服器釋出到Kafka的Topic中,然後消費者透過訂閱這些Topic來做運營資料的實時的監控分析,也可儲存到資料庫;


生產消費基本流程


這些年背過的面試題——Kafka篇

  1. Producer建立時,會建立一個Sender執行緒並設定為守護執行緒。

  2. 生產的訊息先經過攔截器->序列化器->分割槽器,然後將訊息快取在緩衝區。

  3. 批次傳送的條件為:緩衝區資料大小達到batch.size或者linger.ms達到上限。

  4. 批次傳送後,發往指定分割槽,然後落盤到broker;

  • acks=0只要將訊息放到緩衝區,就認為訊息已經傳送完成。

  • acks=1表示訊息只需要寫到主分割槽即可。在該情形下,如果主分割槽收到訊息確認之後就當機了,而副本分割槽還沒來得及同步該訊息,則該訊息丟失。

  • acks=all (預設)首領分割槽會等待所有的ISR副本分割槽確認記錄。該處理保證了只要有一個ISR副本分割槽存活,訊息就不會丟失。

  • 如果生產者配置了retrires引數大於0並且未收到確認,那麼客戶端會對該訊息進行重試。

  • 落盤到broker成功,返回生產後設資料給生產者。


  • Leader選舉


    • Kafka會在Zookeeper上針對每個Topic維護一個稱為ISR(in-sync replica)的集合;

    • 當集合中副本都跟Leader中的副本同步了之後,kafka才會認為訊息已提交;

    • 只有這些跟Leader保持同步的Follower才應該被選作新的Leader;

    • 假設某個topic有N+1個副本,kafka可以容忍N個伺服器不可用,冗餘度較低

      如果ISR中的副本都丟失了,則:

      • 可以等待ISR中的副本任何一個恢復,接著對外提供服務,需要時間等待;

      • 從OSR中選出一個副本做Leader副本,此時會造成資料丟失;


    副本訊息同步
    首先,Follower 傳送 FETCH 請求給 Leader。接著,Leader 會讀取底層日誌檔案中的消 息資料,再更新它記憶體中的 Follower 副本的 LEO 值,更新為 FETCH 請求中的 fetchOffset 值。最後,嘗試更新分割槽高水位值。Follower 接收到 FETCH 響應之後,會把訊息寫入到底層日誌,接著更新 LEO 和 HW 值。
    相關概念:LEO和HW。


    • LEO:即日誌末端位移(log end offset),記錄了該副本日誌中下一條訊息的位移值。如果LEO=10,那麼表示該副本儲存了10條訊息,位移值範圍是[0, 9]。

    • HW:水位值HW(high watermark)即已備份位移。對於同一個副本物件而言,其HW值不會大於LEO值。小於等於HW值的所有訊息都被認為是“已備份”的(replicated)。


    Rebalance


    • 組成員數量發生變化
    • 訂閱主題數量發生變化
    • 訂閱主題的分割槽數發生變化


    leader選舉完成後,當以上三種情況發生時,Leader根據配置的RangeAssignor開始分配消費方案,即哪個consumer負責消費哪些topic的哪些partition。一旦完成分配,leader會將這個方案封裝進SyncGroup請求中發給coordinator,非leader也會發SyncGroup請求,只是內容為空。coordinator接收到分配方案之後會把方案塞進SyncGroup的response中發給各個consumer。這樣組內的所有成員就都知道自己應該消費哪些分割槽了。
    分割槽分配演算法RangeAssignor


    • 原理是按照消費者總數和分割槽總數進行整除運算平均分配給所有的消費者;

    • 訂閱Topic的消費者按照名稱的字典序排序,分均分配,剩下的字典序從前往後分配;


    增刪改查



    kafka-topics.sh --zookeeper localhost:2181/myKafka --create --topic topic_x                                 --partitions 1 --replication-factor 1kafka-topics.sh --zookeeper localhost:2181/myKafka --delete --topic topic_xkafka-topics.sh --zookeeper localhost:2181/myKafka --alter --topic topic_x                                --config max.message.bytes=1048576kafka-topics.sh --zookeeper localhost:2181/myKafka --describe --topic topic_x


    如何檢視偏移量為23的訊息?
    透過查詢跳躍表ConcurrentSkipListMap,定位到在00000000000000000000.index ,透過二分法在偏移量索引檔案中找到不大於 23 的最大索引項,即offset 20 那欄,然後從日誌分段檔案中的物理位置為320 開始順序查詢偏移量為 23 的訊息。


    這些年背過的面試題——Kafka篇


    切分檔案


    • 大小分片 當前日誌分段檔案的大小超過了 broker 端引數 log.segment.bytes 配置的值;

    • 時間分片 當前日誌分段中訊息的最大時間戳與系統的時間戳的差值大於log.roll.ms配置的值;

    • 索引分片 偏移量或時間戳索引檔案大小達到broker端 log.index.size.max.bytes配置的值;

    • 偏移分片 追加的訊息的偏移量與當前日誌分段的偏移量之間的差值大於 Integer.MAX_VALUE;


    一致性

    冪等性
    保證在訊息重發的時候,消費者不會重複處理。即使在消費者收到重複訊息的時候,重複處理,也保證最終結果的一致性。所謂冪等性,數學概念就是:f(f(x)) = f(x) 


    這些年背過的面試題——Kafka篇


    如何實現?
    新增唯一ID,類似於資料庫的主鍵,用於唯一標記一個訊息。



    ProducerID:#在每個新的Producer初始化時,會被分配一個唯一的PIDSequenceNumber:#對於每個PID傳送資料的每個Topic都對應一個從0開始單調遞增的SN值

    這些年背過的面試題——Kafka篇


    如何選舉


    1. 使用 Zookeeper 的分散式鎖選舉控制器,並在節點加入叢集或退出叢集時通知控制器。

    2. 控制器負責在節點加入或離開叢集時進行分割槽Leader選舉。

    3. 控制器使用epoch忽略小的紀元來避免腦裂:兩個節點同時認為自己是當前的控制器。


    可用性


    • 建立Topic的時候可以指定 --replication-factor 3 ,表示不超過broker的副本數

    • 只有Leader是負責讀寫的節點,Follower定期地到Leader上Pull資料。

    • ISR是Leader負責維護的與其保持同步的Replica列表,即當前活躍的副本列表。如果一個Follow落後太多,Leader會將它從ISR中移除。選舉時優先從ISR中挑選Follower。 

    • 設定 acks=all 。Leader收到了ISR中所有Replica的ACK,才向Producer傳送ACK。


    面試題


    線上問題rebalance


    因叢集架構變動導致的消費組內重平衡,如果kafka集內節點較多,比如數百個,那重平衡可能會耗時導致數分鐘到數小時,此時kafka基本處於不可用狀態,對kafka的TPS影響極大。


    產生的原因:


    • 組成員數量發生變化

    • 訂閱主題數量發生變化

    • 訂閱主題的分割槽數發生變化

      組成員崩潰和組成員主動離開是兩個不同的場景。因為在崩潰時成員並不會主動地告知coordinator此事,coordinator有可能需要一個完整的session.timeout週期(心跳週期)才能檢測到這種崩潰,這必然會造成consumer的滯後。可以說離開組是主動地發起rebalance;而崩潰則是被動地發起rebalance。

    • 這些年背過的面試題——Kafka篇



    解決方案:

    加大超時時間 session.timout.ms=6s加大心跳頻率 heartbeat.interval.ms=2s增長推送間隔 max.poll.interval.ms=t+1 minutes


    ZooKeeper 的作用

    目前,Kafka 使用 ZooKeeper 存放叢集後設資料、成員管理、Controller 選舉,以及其他一些管理類任務。之後,等 KIP-500 提案完成後,Kafka 將完全不再依賴於 ZooKeeper。


    • 存放後設資料是指主題分割槽的所有資料都儲存在 ZooKeeper 中,其他“人”都要與它保持對齊。

    • 成員管理是指 Broker 節點的註冊、登出以及屬性變更等 。

    • Controller 選舉是指選舉叢集 Controller,包括但不限於主題刪除、引數配置等。


    一言以蔽之:KIP-500 ,是使用社群自研的基於 Raft 的共識演算法,實現 Controller 自選舉。
    同樣是儲存後設資料,這幾年基於Raft演算法的etcd認可度越來越高。
    越來越多的系統開始用它儲存關鍵資料。比如,秒殺系統經常用它儲存各節點資訊,以便控制消費 MQ 的服務數量。還有些業務系統的配置資料,也會透過 etcd 實時同步給業務系統的各節點,比如,秒殺管理後臺會使用 etcd 將秒殺活動的配置資料實時同步給秒殺 API 服務各節點。


    Replica副本的作用

    Kafka 只有 Leader 副本才能 對外提供讀寫服務,響應 Clients 端的請求。Follower 副本只是採用拉(PULL)的方 式,被動地同步 Leader 副本中的資料,並且在 Leader 副本所在的 Broker 當機後,隨時準備應聘 Leader 副本。


    • 自 Kafka 2.4 版本開始,社群可以透過配置引數,允許 Follower 副本有限度地提供讀服務。

    • 之前確保一致性的主要手段是高水位機制, 但高水位值無法保證 Leader 連續變更場景下的資料一致性,因此,社群引入了 Leader Epoch 機制,來修復高水位值的弊端。



    為什麼不支援讀寫分離?


    • 自 Kafka 2.4 之後,Kafka 提供了有限度的讀寫分離。

    • 場景不適用。讀寫分離適用於那種讀負載很大,而寫操作相對不頻繁的場景。

    • 同步機制。Kafka 採用 PULL 方式實現 Follower 的同步,同時複製延遲較大。



    如何防止重複消費


    • 程式碼層面每次消費需提交offset;

    • 透過Mysql的唯一鍵約束,結合Redis檢視id是否被消費,存Redis可以直接使用set方法;

    • 量大且允許誤判的情況下,使用布隆過濾器也可以;



    如何保證資料不會丟失


    • 生產者生產訊息可以透過comfirm配置ack=all解決;

    • Broker同步過程中leader當機可以透過配置ISR副本+重試解決;

    • 消費者丟失可以關閉自動提交offset功能,系統處理完成時提交offset;



    如何保證順序消費


    • 單 topic,單partition,單 consumer,單執行緒消費,吞吐量低,不推薦;

    • 如只需保證單key有序,為每個key申請單獨記憶體 queue,每個執行緒分別消費一個記憶體 queue 即可,這樣就能保證單key(例如使用者id、活動id)順序性。



    【線上】如何解決積壓消費


    • 修復consumer,使其具備消費能力,並且擴容N臺;

    • 寫一個分發的程式,將Topic均勻分發到臨時Topic中;

    • 同時起N臺consumer,消費不同的臨時Topic;



    如何避免訊息積壓


    • 提高消費並行度
    • 批次消費
    • 減少元件IO的互動次數
    • 優先順序消費



    if (maxOffset - curOffset > 100000) {  // TODO 訊息堆積情況的優先處理邏輯  // 未處理的訊息可以選擇丟棄或者打日誌  return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}// TODO 正常消費過程return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;


    如何設計訊息佇列

    需要支援快速水平擴容,broker+partition,partition放不同的機器上,增加機器時將資料根據topic做遷移,分散式需要考慮一致性、可用性、分割槽容錯性


    • 一致性:生產者的訊息確認、消費者的冪等性、Broker的資料同步;

    • 可用性:資料如何保證不丟不重、資料如何持久化、持久化時如何讀寫;

    • 分割槽容錯:採用何種選舉機制、如何進行多副本同步;

    • 海量資料:如何解決訊息積壓、海量Topic效能下降;


    效能上,可以借鑑時間輪、零複製、IO多路複用、順序讀寫、壓縮批處理



    來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70027827/viewspace-3006812/,如需轉載,請註明出處,否則將追究法律責任。

    相關文章