天天看點

Kafka 會不會丢消息?

一、認識 Kafka

Kafka 是分布式釋出-訂閱消息系統。它最初由 LinkedIn 公司開發,之後成為 Apache 項目的一部分。

Kafka 是一個分布式的,可劃分的,備援備份的持久性的日志服務。它主要用于處理活躍的流式資料。

1. kafka 架構

Kafka 的整體架構非常簡單,是顯式分布式架構,主要由 producer、broker(kafka)和 consumer 組成。

Kafka 會不會丢消息?

Producer(生産者)可以将資料釋出到所選擇的 topic(主題)中。生産者負責将記錄配置設定到 topic 的哪一個 partition(分區)中。可以使用循環的方式來簡單地實作負載均衡,也可以根據某些語義分區函數(如記錄中的key)來完成。

Consumer(消費者)使用一個consumer group(消費組)名稱來進行辨別,釋出到 topic 中的每條記錄被配置設定給訂閱消費組中的一個消費者執行個體。消費者執行個體可以分布在多個程序中或者多個機器上。

二、Kafka 到底會不會丢失消息?

在讨論 kafka 是否丢消息前先來了解一下什麼是消息傳遞語義。

Kafka 會不會丢消息?

message delivery semantic 也就是消息傳遞語義,簡單說就是消息傳遞過程中消息傳遞的保證性。主要分為三種:

  • at most once:最多一次。消息可能丢失也可能被處理,但最多隻會被處理一次。
  • at least once:至少一次。消息不會丢失,但可能被處理多次。可能重複,不會丢失。
  • exactly once:精确傳遞一次。消息被處理且隻會被處理一次。不丢失不重複就一次。

理想情況下肯定是希望系統的消息傳遞是嚴格 exactly once,也就是保證不丢失、隻會被處理一次,但是很難做到。

回到主角 Kafka,Kafka 有三次消息傳遞的過程:

  • 生産者發消息給 Kafka Broker。
  • Kafka Broker 消息同步和持久化
  • Kafka Broker 将消息傳遞給消費者。

在這三步中每一步都有可能會丢失消息,下面詳細分析為什麼會丢消息,如何最大限度避免丢失消息。

三、生産者丢失消息

先介紹一下生産者發送消息的一般流程(部分流程與具體配置項強相關,這裡先忽略):

  1. 生産者是與 leader 直接互動,是以先從叢集擷取 topic 對應分區的 leader 中繼資料;
  2. 擷取到 leader 分區中繼資料後直接将消息發給過去;
  3. Kafka Broker 對應的 leader 分區收到消息後寫入檔案持久化;
  4. Follower 拉取 Leader 消息與 Leader 的資料保持一緻;
  5. Follower 消息拉取完畢需要給 Leader 回複 ACK 确認消息;
  6. Kafka Leader 和 Follower 分區同步完,Leader 分區會給生産者回複 ACK 确認消息。
Kafka 會不會丢消息?

生産者采用 push 模式将資料釋出到 broker,每條消息追加到分區中,順序寫入磁盤。消息寫入 Leader 後,Follower 是主動與 Leader 進行同步。

Kafka 消息發送有兩種方式:同步(sync)和異步(async),預設是同步方式,可通過 producer.type 屬性進行配置。

Kafka 通過配置 request.required.acks 屬性來确認 Producer 的消息:

  • 0:表示不進行消息接收是否成功的确認;不能保證消息是否發送成功,生成環境基本不會用。
  • 1:預設值,表示當 Leader 接收成功時确認;隻要 Leader 存活就可以保證不丢失,保證了吞吐量。是以預設的 producer 級别是 at least once。
  • all:保證 leader 和 follower 不丢,但是如果網絡擁塞,沒有收到 ACK,會有重複發的問題。

如果 acks 配置為 0,發生網絡抖動消息丢了,生産者不校驗 ACK 自然就不知道丢了。

如果 acks 配置為 1 保證 leader 不丢,但是如果 leader 挂了,恰好選了一個沒有 ACK 的 follower,那也丢了。

如果 acks 配置為 all 保證 leader 和 follower 不丢,但是如果網絡擁塞,沒有收到 ACK,會有重複發的問題。

四、Kafka Broker 丢失消息

Kafka Broker 接收到資料後會将資料進行持久化存儲,你以為是下面這樣的:

Kafka 會不會丢消息?

沒想到是這樣的:

Kafka 會不會丢消息?

作業系統本身有一層緩存,叫做 Page Cache,當往磁盤檔案寫入的時候,系統會先将資料流寫入緩存中,至于什麼時候将緩存的資料寫入檔案中是由作業系統自行決定。

Kafka 提供了一個參數 producer.type 來控制是不是主動 flush,如果 Kafka 寫入到 mmap 之後就立即 flush 然後再傳回 Producer 叫同步 (sync);寫入 mmap 之後立即傳回 Producer 不調用 flush 叫異步 (async)。

Kafka 通過多分區多副本機制中已經能最大限度保證資料不會丢失,如果資料已經寫入系統 cache 中但是還沒來得及刷入磁盤,此時突然機器當機或者掉電那就丢了,當然這種情況很極端。

五、消費者丢失消息

消費者通過 pull 模式主動的去 kafka 叢集拉取消息,與 producer 相同的是,消費者在拉取消息的時候也是找 leader 分區去拉取。

多個消費者可以組成一個消費者組(consumer group),每個消費者組都有一個組id。同一個消費者組的消費者可以消費同一 topic 下不同分區的資料,但是不會出現多個消費者消費同一分區的資料。

Kafka 會不會丢消息?

消費者消費的進度通過 offset 儲存在 kafka 叢集的 __consumer_offsets 這個 topic 中。

消費消息的時候主要分為兩個階段:

  1. 辨別消息已被消費,commit offset坐标;
  2. 處理消息。

先 commit 再處理消息。如果在處理消息的時候異常了,但是 offset 已經送出了,這條消息對于該消費者來說就是丢失了,再也不會消費到了。

先處理消息再 commit。如果在 commit 之前發生異常,下次還會消費到該消息,重複消費的問題可以通過業務保證消息幂等性來解決。

六、總結

那麼問題來了,kafka到底會不會丢消息?答案是:會!

Kafka可能會在三個階段丢失消息:

  1. 生産者發送資料;
  2. Kafka Broker 存儲資料;
  3. 消費者消費資料;

在生産環境中嚴格做到 exactly once 其實是難的,同時也會犧牲效率和吞吐量,最佳實踐是業務側做好補償機制,萬一出現消息丢失可以兜底。

原文連結:面試被問:Kafka 會不會丢消息?我是這麼答的