kafka高頻面試題

1、如何獲取 topic 主題的列表

bin/kafka-topics.sh --list --zookeeper localhost:2181

2、生產者和消費者的命令行是什麼?

生產者在主題上發佈消息:

bin/kafka-console-producer.sh --broker-list 192.168.43.49:9092 --topicHello-Kafka

注意這裏的 IP 是 server.properties 中的 listeners 的配置。接下來每個新行就是輸入一條新消息。

消費者接受消息:

bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topicHello-Kafka --from-beginning

3、consumer 是推還是拉?

Kafka 最初考慮的問題是,customer 應該從 brokes 拉取消息還是 brokers 將消息推送到 consumer,也就是 pull 還 push。在這方面,Kafka 遵循了一種大部分消息系統共同的傳統的設計:producer 將消息推送到 broker,consumer 從broker 拉取消息。

一些消息系統比如 Scribe 和 Apache Flume 採用了 push 模式,將消息推送到下游的 consumer。這樣做有好處也有壞處:由 broker 決定消息推送的速率,對於不同消費速率的 consumer 就不太好處理了。消息系統都致力於讓 consumer 以最大的速率最快速的消費消息,但不幸的是,push 模式下,當 broker 推送的速率遠大於 consumer 消費的速率時,consumer 恐怕就要崩潰了。最終 Kafka 還是選取了傳統的 pull 模式。

Pull 模式的另外一個好處是 consumer 可以自主決定是否批量的從 broker 拉取數據 。Push 模式必須在不知道下游 consumer 消費能力和消費策略的情況下決定是立即推送每條消息還是緩存之後批量推送。如果爲了避免 consumer 崩潰而採用較低的推送速率,將可能導致一次只推送較少的消息而造成浪費。Pull 模式下,consumer 就可以根據自己的消費能力去決定這些策略。

Pull 有個缺點是,如果 broker 沒有可供消費的消息,將導致 consumer 不斷在循環中輪詢,直到新消息到 t 達。爲了避免這點,Kafka 有個參數可以讓 consumer阻塞知道新消息到達(當然也可以阻塞知道消息的數量達到某個特定的量這樣就可以批量發送)。

4、講講 kafka 維護消費狀態跟蹤的方法

大部分消息系統在 broker 端的維護消息被消費的記錄:一個消息被分發到consumer 後 broker 就馬上進行標記或者等待 customer 的通知後進行標記。這樣也可以在消息在消費後立馬就刪除以減少空間佔用。

但是這樣會不會有什麼問題呢?如果一條消息發送出去之後就立即被標記爲消費過的,旦 consumer 處理消息時失敗了(比如程序崩潰)消息就丟失了。爲了解決這個問題,很多消息系統提供了另外一個個功能:當消息被髮送出去之後僅僅被標記爲已發送狀態,當接到 consumer 已經消費成功的通知後才標記爲已被消費的狀態。這雖然解決了消息丟失的問題,但產生了新問題,首先如果 consumer處理消息成功了但是向 broker 發送響應時失敗了,這條消息將被消費兩次。第二個問題時,broker 必須維護每條消息的狀態,並且每次都要先鎖住消息然後更改狀態然後釋放鎖。這樣麻煩又來了,且不說要維護大量的狀態數據,比如如果消息發送出去但沒有收到消費成功的通知,這條消息將一直處於被鎖定的狀態,Kafka 採用了不同的策略。Topic 被分成了若干分區,每個分區在同一時間只被一個 consumer 消費。這意味着每個分區被消費的消息在日誌中的位置僅僅是一個簡單的整數:offset。這樣就很容易標記每個分區消費狀態就很容易了,僅僅需要一個整數而已。這樣消費狀態的跟蹤就很簡單了。

這帶來了另外一個好處:consumer 可以把 offset 調成一個較老的值,去重新消費老的消息。這對傳統的消息系統來說看起來有些不可思議,但確實是非常有用的,誰規定了一條消息只能被消費一次呢?

5、講一下主從同步

Kafka允許topic的分區擁有若干副本,這個數量是可以配置的,你可以爲每個topci配置副本的數量。Kafka會自動在每個個副本上備份數據,所以當一個節點down掉時數據依然是可用的。

Kafka的副本功能不是必須的,你可以配置只有一個副本,這樣其實就相當於只有一份數據。

6、爲什麼需要消息系統,mysql 不能滿足需求嗎?

(1)解耦:

允許你獨立的擴展或修改兩邊的處理過程,只要確保它們遵守同樣的接口約束。

(2)冗餘:

消息隊列把數據進行持久化直到它們已經被完全處理,通過這一方式規避了數據丟失風險。許多消息隊列所採用的”插入-獲取-刪除”範式中,在把一個消息從隊列中刪除之前,需要你的處理系統明確的指出該消息已經被處理完畢,從而確保你的數據被安全的保存直到你使用完畢。

(3)擴展性:

因爲消息隊列解耦了你的處理過程,所以增大消息入隊和處理的頻率是很容易的,只要另外增加處理過程即可。

(4)靈活性 & 峯值處理能力:

在訪問量劇增的情況下,應用仍然需要繼續發揮作用,但是這樣的突發流量並不常見。如果爲以能處理這類峯值訪問爲標準來投入資源隨時待命無疑是巨大的浪費。使用消息隊列能夠使關鍵組件頂住突發的訪問壓力,而不會因爲突發的超負荷的請求而完全崩潰。

(5)可恢復性:

系統的一部分組件失效時,不會影響到整個系統。消息隊列降低了進程間的耦合度,所以即使一個處理消息的進程掛掉,加入隊列中的消息仍然可以在系統恢復後被處理。

(6)順序保證:

在大多使用場景下,數據處理的順序都很重要。大部分消息隊列本來就是排序的,並且能保證數據會按照特定的順序來處理。(Kafka 保證一個 Partition 內的消息的有序性)

(7)緩衝:

有助於控制和優化數據流經過系統的速度,解決生產消息和消費消息的處理速度不一致的情況。

(8)異步通信:

很多時候,用戶不想也不需要立即處理消息。消息隊列提供了異步處理機制,允許用戶把一個消息放入隊列,但並不立即處理它。想向隊列中放入多少消息就放多少,然後在需要的時候再去處理它們。

7、Zookeeper 對於 Kafka 的作用是什麼?

Zookeeper 是一個開放源碼的、高性能的協調服務,它用於 Kafka 的分佈式應用。

Zookeeper 主要用於在集羣中不同節點之間進行通信

在 Kafka 中,它被用於提交偏移量,因此如果節點在任何情況下都失敗了,它都可以從之前提交的偏移量中獲取除此之外,它還執行其他活動,如: leader 檢測、分佈式同步、配置管理、識別新節點何時離開或連接、集羣、節點實時狀態等等。

8、數據傳輸的事務定義有哪三種?

和 MQTT 的事務定義一樣都是 3 種。

(1)最多一次: 消息不會被重複發送,最多被傳輸一次,但也有可能一次不傳輸

(2)最少一次: 消息不會被漏發送,最少被傳輸一次,但也有可能被重複傳輸.

(3)精確的一次(Exactly once): 不會漏傳輸也不會重複傳輸,每個消息都傳輸被一次而且僅僅被傳輸一次,這是大家所期望的

9、Kafka 判斷一個節點是否還活着有那兩個條件?

(1)節點必須可以維護和 ZooKeeper 的連接,Zookeeper 通過心跳機制檢查每個節點的連接

(2)如果節點是個 follower,他必須能及時的同步 leader 的寫操作,延時不能太久

10、Kafka 與傳統 MQ 消息系統之間有三個關鍵區別

(1).Kafka 持久化日誌,這些日誌可以被重複讀取和無限期保留

(2).Kafka 是一個分佈式系統:它以集羣的方式運行,可以靈活伸縮,在內部通過複製數據提升容錯能力和高可用性

(3).Kafka 支持實時的流式處理

11、講一講 kafka 的 ack 的三種機制

request.required.acks 有三個值 0 1 -1(all)

0:生產者不會等待 broker 的 ack,這個延遲最低但是存儲的保證最弱當 server 掛掉的時候就會丟數據。

1:服務端會等待 ack 值 leader 副本確認接收到消息後發送 ack 但是如果 leader掛掉後他不確保是否複製完成新 leader 也會導致數據丟失。

-1(all):服務端會等所有的 follower 的副本受到數據後纔會受到 leader 發出的ack,這樣數據不會丟失

12、消費者如何不自動提交偏移量,由應用提交?

將 auto.commit.offset 設爲 false,然後在處理一批消息後 commitSync() 或者異步提交 commitAsync()

即:

ConsumerRecords<> records = consumer.poll();
for (ConsumerRecord<> record : records){
	。。。
	tyr{
		consumer.commitSync()
	}
	。。。
}

13、消費者故障,出現活鎖問題如何解決?

出現“活鎖”的情況,是它持續的發送心跳,但是沒有處理。爲了預防消費者在這種情況下一直持有分區,我們使用 max.poll.interval.ms 活躍檢測機制。 在此基礎上,如果你調用的 poll 的頻率大於最大間隔,則客戶端將主動地離開組,以便其他消費者接管該分區。 發生這種情況時,你會看到 offset 提交失敗(調用commitSync()引發的 CommitFailedException)。這是一種安全機制,保障只有活動成員能夠提交 offset。所以要留在組中,你必須持續調用 poll。

消費者提供兩個配置設置來控制 poll 循環:

max.poll.interval.ms:增大 poll 的間隔,可以爲消費者提供更多的時間去處理返回的消息(調用 poll(long)返回的消息,通常返回的消息都是一批)。缺點是此值越大將會延遲組重新平衡。

max.poll.records:此設置限制每次調用 poll 返回的消息數,這樣可以更容易的預測每次 poll 間隔要處理的最大值。通過調整此值,可以減少 poll 間隔,減少重新平衡分組的

對於消息處理時間不可預測地的情況,這些選項是不夠的。 處理這種情況的推薦方法是將消息處理移到另一個線程中,讓消費者繼續調用 poll。 但是必須注意確保已提交的 offset 不超過實際位置。另外,你必須禁用自動提交,並只有在線程完成處理後才爲記錄手動提交偏移量(取決於你)。 還要注意,你需要 pause 暫停分區,不會從 poll 接收到新消息,讓線程處理完之前返回的消息(如果你的處理能力比拉取消息的慢,那創建新線程將導致你機器內存溢出)。

14、如何控制消費的位置

kafka 使用 seek(TopicPartition, long)指定新的消費位置。用於查找服務器保留的最早和最新的 offset 的特殊的方法也可用(seekToBeginning(Collection) 和seekToEnd(Collection))

15、kafka 分佈式(不是單機)的情況下,如何保證消息的順序消費?

Kafka 分佈式的單位是 partition,同一個 partition 用一個 write ahead log 組織,所以可以保證 FIFO 的順序。不同 partition 之間不能保證順序。但是絕大多數用戶都可以通過 message key 來定義,因爲同一個 key 的 message 可以保證只發送到同一個 partition。

Kafka 中發送 1 條消息的時候,可以指定(topic, partition, key) 3 個參數。partiton 和 key 是可選的。如果你指定了 partition,那就是所有消息發往同 1個 partition,就是有序的。並且在消費端,Kafka 保證,1 個 partition 只能被1 個 consumer 消費。或者你指定 key( 比如 order id),具有同 1 個 key 的所有消息,會發往同 1 個 partition。

16、kafka 的高可用機制是什麼?

這個問題比較系統,回答出 kafka 的系統特點,leader 和 follower 的關係,消息讀寫的順序即可。

17、kafka 如何減少數據丟失

Kafka到底會不會丟數據(data loss)? 通常不會,但有些情況下的確有可能會發生。下面的參數配置及Best practice列表可以較好地保證數據的持久性(當然是trade-off,犧牲了吞吐量)。

  • block.on.buffer.full = true

  • acks = all

  • retries = MAX_VALUE

  • max.in.flight.requests.per.connection = 1

  • 使用KafkaProducer.send(record, callback)

  • callback邏輯中顯式關閉producer:close(0)

  • unclean.leader.election.enable=false

  • replication.factor = 3

  • min.insync.replicas = 2

  • replication.factor > min.insync.replicas

  • enable.auto.commit=false

  • 消息處理完成之後再提交位移

18、kafka 如何不消費重複數據?比如扣款,我們不能重複的扣。

其實還是得結合業務來思考,我這裏給幾個思路:

比如你拿個數據要寫庫,你先根據主鍵查一下,如果這數據都有了,你就別插入了,update 一下好吧。

比如你是寫 Redis,那沒問題了,反正每次都是 set,天然冪等性。

比如你不是上面兩個場景,那做的稍微複雜一點,你需要讓生產者發送每條數據的時候,裏面加一個全局唯一的 id,類似訂單 id 之類的東西,然後你這裏消費到了之後,先根據這個 id 去比如 Redis 裏查一下,之前消費過嗎?如果沒有消費過,你就處理,然後這個 id 寫 Redis。如果消費過了,那你就別處理了,保證別重複處理相同的消息即可。

比如基於數據庫的唯一鍵來保證重複數據不會重複插入多條。因爲有唯一鍵約束了,重複數據插入只會報錯,不會導致數據庫中出現髒數據。

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