1、什麼是Kafka?
Kafka是一個 Scala 語言開發的多分割槽、多副本、分散式的基於釋出/訂閱模式的訊息佇列。目前 Kafka 已經定位為一個分散式流式處理平臺,它以高吞吐、可持久化、可水平擴充套件、支援流資料處理等多種特性而被廣泛使用。
2、Kafka架構
Kafak 總體架構圖中包含多個概念:
(1)ZooKeeper: Zookeeper 負責儲存 broker 叢集後設資料,並對控制器進行選舉等操作。
(2)Producer: 訊息生產者,就是向 kafka broker 發訊息的客戶端。
(3)Broker: 一個獨立的 Kafka 伺服器被稱作 broker,一個叢集由多個 broker 組成,一個 broker 可以容納多個 topic。broker負責接收來自生產者的訊息,為訊息設定偏移量,並將訊息儲存在磁碟。broker 為消費者提供服務,對讀取分割槽的請求作出響應,返回已經提交到磁碟上的訊息。
(4)Consumer: 訊息消費者,向 kafka broker 取訊息的客戶端。
(5)Consumer Group:消費者組,一個消費者組可以包含一個或多個 Consumer。消費者組內每個消費者負責消費不同分割槽的資料,一個分割槽只能由一個組內消費者消費。消費者組之間互不影響。所有的消費者都屬於某個消費者組,即消費者組是邏輯上的一個訂閱者。使用多分割槽+多消費者方式可以極大提高資料下游的處理速度,同一消費者組中的消費者不會重複消費訊息,同樣的,不同消費組中的消費者消費訊息時互不影響。Kafka 就是通過消費者組的方式來實現訊息 P2P 模式和廣播模式。
(6)Topic: Kafka 中的訊息以 Topic 為單位進行劃分,可以理解為一個佇列。生產者將訊息傳送到特定的 Topic,而消費者負責訂閱 Topic 的訊息並進行消費。
(7)Partition: 為了實現擴充套件性,一個非常大的 topic 可以分佈到多個 broker(伺服器)上,一個 topic 可以分為多個 partition,每個 partition 是一個有序的佇列。同一個主題下不同分割槽包含的訊息是不同的,分割槽在儲存層面可以看作一個可追加的日誌(Log)檔案,訊息在被
追加到分割槽日誌檔案的時候都會分配一個特定的偏移量(offset)。
(8)Offset: 分割槽中每條訊息都會分配一個有序的id,即偏移量。offset 不跨越分割槽,也就是說 Kafka 保證的是分割槽有序性而不是主題有序性。
(9)Replica: 副本,為保證叢集中的某個節點發生故障時,該節點上的 partition 資料不丟失,且 kafka 仍然能夠繼續工作,kafka 提供了副本機制,一個 topic 的每個分割槽都有若干個副本,一個 leader 和若干個 follower。通常只有 leader 副本對外提供讀寫服務,當主副本所在 broker 崩潰或發生網路異常,Kafka 會在 Controller 的管理下會重新選擇新的 leader 副本對外提供讀寫服務。
(10)Record: 實際寫入 Kafka 中並可以被讀取的訊息記錄。每個 record 包含了key、value 和 timestamp。
(11)Leader: 每個分割槽多個副本的 "主" 副本,生產者傳送資料的物件,以及消費者消費資料的物件都是 leader。
(12)Follower: 每個分割槽多個副本中的"從" 副本,實時從 Leader 中同步資料,保持和 leader 資料的同步。Leader 發生故障時,某個 follow 會成為新的 leader。
(13)ISR(In-Sync Replicas):副本同步佇列,表示和 leader 保持同步的副本的集合(包括leader本身)。如果 follower 長時間不與 leader 同步資料則將該副本踢出ISR佇列。leader發生故障會從ISR中選舉新leader。
(14)OSR(Out-of-Sync Replicas):因同步延遲過高而被踢出ISR的副本存在OSR。
(15)AR(Assigned Replicas):所有副本集合,即 AR = ISR + OSR 。
3、釋出訂閱的訊息系統那麼多,為啥選擇 Kafka?(Kafka的特點)
(1)多個生產者
KafKa 可以無縫地支援多個生產者,不管客戶端使用一個主題,還是多個主題。Kafka 適合從多個前端系統收集資料,並以統一的格式堆外提供資料。
(2)多個消費者
Kafka 支援多個消費者從一個單獨的訊息流中讀取資料,並且消費者之間互不影響。這與其他佇列系統不同,其他佇列系統一旦被客戶端讀取,其他客戶端就不能再讀取它。並且多個消費者可以組成一個消費者組,他們共享一個訊息流,並保證消費者組對每個給定的訊息只消費一次。
(3)基於磁碟的資料儲存(永續性,可靠性)
Kafka 允許消費者非實時地讀取訊息,原因在於 Kafka 將訊息提交到磁碟上,設定了保留規則進行儲存,無需擔心訊息丟失等問題。
(4)伸縮性,可擴充套件性
可擴充套件多臺 broker。使用者可以先使用單個 broker,到後面可以擴充套件到多個 broker
(5)高效能(高吞吐,低延遲)
Kafka 可以輕鬆處理百萬千萬級訊息流,同時還能保證亞秒級的訊息延遲。
4、kafka 如何做到高吞吐量/高效能的?
Kafka 實現高吞吐量和效能,主要通過以下幾點:
1、頁快取技術
Kafka 是基於 作業系統 的頁快取來實現檔案寫入的。作業系統本身有一層快取,叫做 page cache,是在 記憶體裡的快取,我們也可以稱之為 os cache,意思就是作業系統自己管理的快取。Kafka 在寫入磁碟檔案的時候,可以直接寫入這個 os cache 裡,也就是僅僅寫入記憶體中,接下來由作業系統自己決定什麼時候把 os cache 裡的資料真的刷入磁碟檔案中。通過這一個步驟,就可以將磁碟檔案寫效能提升很多了,因為其實這裡相當於是在寫記憶體,不是在寫磁碟。
2、磁碟順序寫
另一個主要功能是 kafka 寫資料的時候,是以磁碟順序寫的方式來寫的。也就是說,僅僅將資料追加到log檔案的末尾,不是在檔案的隨機位置來修改資料。同樣的磁碟,順序寫能到 600M/s,而隨機寫只有 100K/s。這與磁碟的機械機構有關,順序寫之所以快,是因為其省去了大量磁頭定址的時間。
基於上面兩點,kafka 就實現了寫入資料的超高效能。
3、零拷貝
大家應該都知道,從 Kafka 裡經常要消費資料,那麼消費的時候實際上就是要從 Kafka 的磁碟檔案裡讀取某條資料然後傳送給下游的消費者,如下圖所示:
那麼這裡如果頻繁的從磁碟讀資料然後發給消費者,會增加兩次沒必要的拷貝,如下圖:
一次是從作業系統的 cache 裡拷貝到應用程式的快取裡,接著又從應用程式快取裡拷貝回作業系統的 Socket 快取裡。而且為了進行這兩次拷貝,中間還發生了好幾次上下文切換,一會兒是應用程式在執行,一會兒上下文切換到作業系統來執行。所以這種方式來讀取資料是比較消耗效能的。
Kafka 為了解決這個問題,在讀資料的時候是引入零拷貝技術。
也就是說,直接讓作業系統的 cache 中的資料傳送到網路卡後傳輸給下游的消費者,中間跳過了兩次拷貝資料的步驟,Socket 快取中僅僅會拷貝一個描述符過去,不會拷貝資料到 Socket 快取,如下圖所示:
通過 零拷貝技術,就不需要把 os cache 裡的資料拷貝到應用快取,再從應用快取拷貝到 Socket 快取了,兩次拷貝都省略了,所以叫做零拷貝。對 Socket 快取僅僅就是拷貝資料的描述符過去,然後資料就直接從 os cache 中傳送到網路卡上去了,這個過程大大的提升了資料消費時讀取檔案資料的效能。Kafka 從磁碟讀資料的時候,會先看看 os cache 記憶體中是否有,如果有的話,其實讀資料都是直接讀記憶體的。Kafka 叢集經過良好的調優,資料直接寫入 os cache 中,然後讀資料的時候也是從os cache 中讀。相當於 Kafka 完全基於記憶體提供資料的寫和讀了,所以這個整體效能會極其的高。
5、 Kafka 和 Zookeeper 之間的關係
Kafka 使用 Zookeeper 來儲存叢集的後設資料資訊和消費者資訊(偏移量),沒有zookeeper,kafka 是工作不起來。 在 zookeeper 上會有一個專門用來進行 Broker伺服器列表記錄的點,節點路徑為/brokers/ids。
每個 Broker 伺服器在啟動時,都會到 Zookeeper 上進行註冊,即建立/brokers/ids/[0-N] 的節點,然後寫入 IP,埠等資訊,Broker 建立的是臨時節點,所以一旦 Broker 上線或者下線,對應 Broker 節點也就被刪除了,因此可以通過 zookeeper 上 Broker 節點的變化來動態表徵 Broker 伺服器的可用性。
6、生產者向 Kafka 傳送訊息的執行流程
如下圖所示:
(1)生產者要往 Kafka 傳送訊息時,需要建立 ProducerRecoder,程式碼如下:
ProducerRecord<String,String> record
= new ProducerRecoder<>("CostomerCountry","Precision Products","France");
try{
producer.send(record);
}catch(Exception e){
e.printStackTrace();
}
(2)ProducerRecoder 物件會包含目標 topic,分割槽內容,以及指定的 key 和 value,在傳送 ProducerRecoder 時,生產者會先把鍵和值物件序列化成位元組陣列,然後在網路上傳輸。
(3)生產者在將訊息傳送到某個 Topic ,需要經過攔截器、序列化器和分割槽器(Partitioner)。
(4)如果訊息 ProducerRecord 沒有指定 partition 欄位,那麼就需要依賴分割槽器,根據 key 這個欄位來計算 partition 的值。分割槽器的作用就是為訊息分配分割槽。
若沒有指定分割槽,且訊息的 key 不為空,則使用 murmur 的 Hash 演算法(非加密型 Hash 函式,具備高運算效能及低碰撞率)來計算分割槽分配。
若沒有指定分割槽,且訊息的 key 也是空,則用輪詢的方式選擇一個分割槽。
(5)分割槽選擇好之後,會將訊息新增到一個記錄批次中,這個批次的所有訊息都會被髮送到相同的 Topic 和 partition 上。然後會有一個獨立的執行緒負責把這些記錄批次傳送到相應的 broker 中。
(6)broker 接收到 Msg 後,會作出一個響應。如果成功寫入 Kafka 中,就返回一個 RecordMetaData 物件,它包含 Topic 和 Partition 資訊,以及記錄在分割槽的 offset。
(7)若寫入失敗,就返回一個錯誤異常,生產者在收到錯誤之後嘗試重新傳送訊息,幾次之後如果還失敗,就返回錯誤資訊。
7、kafka 如何保證對應型別的訊息被寫到相同的分割槽?
通過 訊息鍵 和 分割槽器 來實現,分割槽器為鍵生成一個 offset,然後使用 offset 對主題分割槽進行取模,為訊息選取分割槽,這樣就可以保證包含同一個鍵的訊息會被寫到同一個分割槽上。
如果 ProducerRecord 沒有指定分割槽,且訊息的 key 不為空,則使用 Hash 演算法(非加密型 Hash 函式,具備高運算效能及低碰撞率)來計算分割槽分配。
如果 ProducerRecord 沒有指定分割槽,且訊息的 key 也是空,則用 輪詢 的方式選擇一個分割槽。
8、kafka 檔案儲存機制
在 Kafka 中,一個 Topic 會被分割成多個 Partition,而 Partition 由多個更小的 Segment 的元素組成。Partition 在伺服器上的表現形式就是一個一個的資料夾,每個 partition 資料夾下面會有多組 segment(邏輯分組,並不是真實存在),每個 segment 對應三個檔案:.log檔案、.index檔案、.timeindex檔案。topic是邏輯上的概念,而 partition是物理上的概念,每個 partition對應於多個 log 檔案,該 log 檔案中儲存的就是 producer生產的資料。Producer生產的資料會被不斷追加到該 log 檔案末端,且每條資料都有自己的 offset。消費者組中的每個消費者,都會實時記錄自己消費到了哪個offset,以便出錯恢復時,從上次的位置繼續消費。
Kafka 會根據 log.segment.bytes 的配置來決定單個 Segment 檔案(log)的大小,當寫入資料達到這個大小時就會建立新的 Segment。
9、如何根據 offset 找到對應的 Message?
每個索引項佔用 8 個位元組,分為兩個部分:
(1) relativeOffset: 相對偏移量,表示訊息相對於 baseOffset 的偏移量,佔用4個位元組(relativeOffset = offset - baseOffset),當前索引檔案的檔名即為 baseOffset 的值。
例如:一個日誌片段的 baseOffset 為 32,那麼其檔名就是 00000000000000000032.log,offset=35 的訊息在索引檔案中的 relativeOffset 的值為 35-32=3
(2) position: 實體地址,也就是訊息在日誌分段檔案中對應的物理位置,佔用 4 個位元組。
(1)先找到 offset=3 的 message 所在的 segment檔案(利用二分法查詢),先判斷.index檔名稱offset(baseOffset)是否小於3;
若小於,則繼續二分與下一個.inde檔名稱offset比較;
若大於,則返回上次小於3的.index檔案,這裡找到的就是在第一個segment檔案。
(2)找到的 segment 中的.index檔案,用查詢的offset 減去.index檔名的offset(relativeOffset = offset - baseOffset),也就是00000.index檔案,我們要查詢的offset為3的message在該.index檔案內的索引為3(index採用稀疏儲存的方式,它不會為每一條message都建立索引,而是每隔4k左右,建立一條索引,避免索引檔案佔用過多的空間。缺點是沒有建立索引的offset不能一次定位到message的位置,需要做一次順序掃描,但是掃描的範圍很小)。
(3)根據找到的 relative offset為3的索引,確定message儲存的物理偏移地址為756。
(4)根據物理偏移地址,去.log檔案找相應的Message
同理,我如果想找offset=8對應的Message資料呢?
(1)首先根據二分查詢法找到segment的對應的00000000000000000006.index索引檔案
(2)根據offset=8找到對應的索引檔案中的位置,該位置儲存了一個偏移量326,根據偏移量326在00000000000000000006.log檔案中找到對應的訊息Message-8。
Kafka 的 Message 儲存採用了分割槽,磁碟順序讀寫,分段和稀疏索引等一些手段來達到高效性,在0.9版本之後,offset 已經直接維護在kafka叢集的__consumer_offsets這個topic中。
10、 Producer 傳送的一條 message 中包含哪些資訊?
訊息由 可變長度 的 報頭、可變長度的 不透明金鑰位元組陣列和 可變長度的 不透明值位元組陣列組成。
RecordBatch 是 Kafka 資料的儲存單元,一個 RecordBatch 中包含多個 Record(即我們通常說的一條訊息)。RecordBatch 中各個欄位的含義如下:
一個 RecordBatch 中可以包含多條訊息,即上圖中的 Record,而每條訊息又可以包含多個 Header 資訊,Header 是 Key-Value 形式的。
11、kafka 如何實現訊息有序
生產者:通過分割槽的 leader 副本負責資料以先進先出的順序寫入,來保證訊息順序性。
消費者:同一個分割槽內的訊息只能被一個 group 裡的一個消費者消費,保證分割槽內消費有序。
kafka 每個 partition 中的訊息在寫入時都是有序的,消費時, 每個 partition 只能被每一個消費者組中的一個消費者消費,保證了消費時也是有序的。
整個 kafka 不保證有序。如果為了保證 kafka 全域性有序,那麼設定一個生產者,一個分割槽,一個消費者。
12、kafka 有哪些分割槽演算法?
Kafka包含三種分割槽演算法:
(1)輪詢策略
也稱 Round-robin 策略,即順序分配。比如一個 topic 下有 3 個分割槽,那麼第一條訊息被髮送到分割槽 0,第二條被髮送到分割槽 1,第三條被髮送到分割槽 2,以此類推。當生產第四條訊息時又會重新開始。
輪詢策略是 kafka java 生產者 API 預設提供的分割槽策略。輪詢策略有非常優秀的負載均衡表現,它總是能保證訊息最大限度地被平均分配到所有分割槽上,故預設情況下它是最合理的分割槽策略,也是平時最常用的分割槽策略之一。
(2)隨機策略
也稱 Randomness 策略。所謂隨機就是我們隨意地將訊息放置在任意一個分割槽上,如下圖:
(3)按 key 分配策略
kafka 允許為每條訊息定義訊息鍵,簡稱為 key。一旦訊息被定義了 key,那麼你就可以保證同一個 key 的所有訊息都進入到相同的分割槽裡面,由於每個分割槽下的訊息處理都是有順序的,如下圖所示:
13、Kafka 的預設訊息保留策略
broker 預設的訊息保留策略分為兩種:
日誌片段通過 log.segment.bytes 配置(預設是1GB)
日誌片段通過 log.segment.ms 配置 (預設7天)
14、kafka 如何實現單個叢集間的訊息複製?
Kafka 訊息負責機制只能在單個叢集中進行復制,不能在多個叢集之間進行。
kafka 提供了一個叫做 MirrorMaker 的核心元件,該元件包含一個生產者和一個消費者,兩者之間通過一個佇列進行相連,當消費者從一個叢集讀取訊息,生產者把訊息傳送到另一個叢集。
15、Kafka 訊息確認(ack 應答)機制
為保證 producer 傳送的資料,能可靠的達到指定的 topic ,Producer 提供了訊息確認機制。生產者往 Broker 的 topic 中傳送訊息時,可以通過配置來決定有幾個副本收到這條訊息才算訊息傳送成功。可以在定義 Producer 時通過 acks 引數指定,這個引數支援以下三種值:
(1)acks = 0:producer 不會等待任何來自 broker 的響應。
特點:低延遲,高吞吐,資料可能會丟失。
如果當中出現問題,導致 broker 沒有收到訊息,那麼 producer 無從得知,會造成訊息丟失。
(2)acks = 1(預設值):只要叢集中 partition 的 Leader 節點收到訊息,生產者就會收到一個來自伺服器的成功響應。
如果在 follower 同步之前,leader 出現故障,將會丟失資料。
此時的吞吐量主要取決於使用的是 同步傳送 還是 非同步傳送 ,吞吐量還受到傳送中訊息數量的限制,例如 producer 在收到 broker 響應之前可以傳送多少個訊息。
(3)acks = -1:只有當所有參與複製的節點全部都收到訊息時,生產者才會收到一個來自伺服器的成功響應。
這種模式是最安全的,可以保證不止一個伺服器收到訊息,就算有伺服器發生崩潰,整個叢集依然可以執行。
根據實際的應用場景,選擇設定不同的 acks,以此保證資料的可靠性。
另外,Producer 傳送訊息還可以選擇同步或非同步模式,如果設定成非同步,雖然會極大的提高訊息傳送的效能,但是這樣會增加丟失資料的風險。如果需要確保訊息的可靠性,必須將 producer.type 設定為 sync。
#同步模式
producer.type=sync
#非同步模式
producer.type=async
16、說一下什麼是副本?
kafka 為了保證資料不丟失,從 0.8.0 版本開始引入了分割槽副本機制。在建立 topic 的時候指定 replication-factor,預設副本為 3 。
副本是相對 partition 而言的,一個分割槽中包含一個或多個副本,其中一個為leader 副本,其餘為follower 副本,各個副本位於不同的 broker 節點中。
所有的讀寫操作都是經過 Leader 進行的,同時 follower 會定期地去 leader 上覆制資料。當 Leader 掛掉之後,其中一個 follower 會重新成為新的 Leader。通過分割槽副本,引入了資料冗餘,同時也提供了 Kafka 的資料可靠性。
Kafka 的分割槽多副本架構是 Kafka 可靠性保證的核心,把訊息寫入多個副本可以使 Kafka 在發生崩潰時仍能保證訊息的永續性。
17、Kafka 的 ISR 機制
在分割槽中,所有副本統稱為 AR ,Leader 維護了一個動態的 in-sync replica(ISR),ISR 是指與 leader 副本保持同步狀態的副本集合。當然 leader 副本本身也是這個集合中的一員。
當 ISR 中的 follower 完成資料同步之後, leader 就會給 follower 傳送 ack ,如果其中一個 follower 長時間未向 leader 同步資料,該 follower 將會被踢出 ISR 集合,該時間閾值由 replica.log.time.max.ms 引數設定。當 leader 發生故障後,就會從 ISR 集合中重新選舉出新的 leader。
18、LEO、HW、LSO、LW 分別代表什麼?
LEO :是 LogEndOffset 的簡稱,代表當前日誌檔案中下一條。
HW:水位或水印一詞,也可稱為高水位(high watermark),通常被用在流式處理領域(flink、spark),以表徵元素或事件在基於時間層面上的進展。在 kafka 中,水位的概念與時間無關,而是與位置資訊相關。嚴格來說,它表示的就是位置資訊,即位移(offset)。取 partition 對應的ISR中最小的 LEO作為HW,consumer 最多隻能消費到 HW 所在的上一條資訊。
LSO: 是 LastStableOffset 的簡稱,對未完成的事務而言,LSO 的值等於事務中第一條訊息的位置(firstUnstableOffset),對已完成的事務而言,它的值同HW 相同。
LW: Low Watermark 低水位,代表AR 集合中最小的 logStartOffset 值。
19、如何進行 Leader 副本選舉?
每個分割槽的 leader 會維護一個 ISR 集合,ISR 列表裡面就是 follower 副本的 Borker 編號,只有“跟得上” Leader 的 follower 副本才能加入到 ISR 裡面,這個是通過 replica.lag.time.max.ms 引數配置的。只有 ISR 裡的成員才有被選為 leader 的可能。
所以當 Leader 掛掉了,而且 unclean.leader.election.enable=false 的情況下,Kafka 會從 ISR 列表中選擇 第一個 follower 作為新的 Leader,因為這個分割槽擁有最新的已經 committed 的訊息。通過這個可以保證已經 committed 的訊息的資料可靠性。
20、如何進行 broker Leader 選舉?
(1) 在 kafka 叢集中,會有多個 broker 節點,叢集中第一個啟動的 broker 會通過在 zookeeper 中建立臨時節點 /controller 來讓自己成為控制器,其他 broker 啟動時也會在 zookeeper 中建立臨時節點,但是發現節點已經存在,所以它們會收到一個異常,意識到控制器已經存在,那麼就會在 zookeeper 中建立 watch 物件,便於它們收到控制器變更的通知。
(2) 如果叢集中有一個 broker 發生異常退出了,那麼控制器就會檢查這個 broker 是否有分割槽的副本 leader ,如果有那麼這個分割槽就需要一個新的 leader,此時控制器就會去遍歷其他副本,決定哪一個成為新的 leader,同時更新分割槽的 ISR 集合。
(3) 如果有一個 broker 加入叢集中,那麼控制器就會通過 Broker ID 去判斷新加入的 broker 中是否含有現有分割槽的副本,如果有,就會從分割槽副本中去同步資料。
(4) 叢集中每選舉一次控制器,就會通過 zookeeper 建立一個 controller epoch,每一個選舉都會建立一個更大,包含最新資訊的 epoch,如果有 broker 收到比這個 epoch 舊的資料,就會忽略它們,kafka 也通過這個 epoch 來防止叢集產生“腦裂”。
21、Kafka 事務
Kafka 在 0.11版本引入事務支援,事務可以保證 Kafka 在 Exactly Once 語義的基礎上,生產和消費可以跨分割槽和會話,要麼全部成功,要麼全部失敗。
Producer 事務
為了實現跨分割槽跨會話事務,需要引入一個全域性唯一的 Transaction ID,並將 Producer 獲取的 PID 和 Transaction ID 繫結。這樣當 Producer 重啟後就可以通過正在進行的 Transaction ID 獲取原來的 PID。
為了管理 Transaction,Kafka 引入了一個新的元件 Transaction Coordinator。Producer 就是通過和 Transaction Coordinator 互動獲得 Transaction ID 對應的任務狀態。Transaction Coordinator 還負責將事務所有寫入 Kafka 的一個內部 Topic,這樣即使整個服務重啟,由於事務狀態得到儲存,進行中的事務狀態可以得到恢復,從而繼續進行。
Consumer 事務
上述事務機制主要是從Producer 方面考慮,對於 Consumer 而言,事務的保證就會相對較弱,尤其是無法保證 Commit 的資訊被精確消費。這是由於 Consumer 可以通過 offset 訪問任意資訊,而且不同的Segment File 生命週期不同,同一事務的訊息可能會出現重啟後被刪除的情況。
22、Kafka的消費者組跟分割槽之間有什麼關係?
(1)在 Kafka 中,通過消費者組管理消費者,假設一個主題中包含 4 個分割槽,在一個消費者組中只要一個消費者。那消費者將收到全部 4 個分割槽的訊息。
(2)如果存在兩個消費者,那麼四個分割槽將根據分割槽分配策略分配個兩個消費者。
(3)如果存在四個消費者,將平均分配,每個消費者消費一個分割槽。
(4)如果存在5個消費者,就會出現消費者數量多於分割槽數量,那麼多餘的消費者將會被閒置,不會接收到任何資訊。
23、如何保證每個應用程式都可以獲取到 Kafka 主題中的所有訊息,而不是部分訊息?
為每個應用程式建立一個消費者組,然後往組中新增消費者來伸縮讀取能力和處理能力,每個群組消費主題中的訊息時,互不干擾。
24、如何實現 kafka 消費者每次只消費指定數量的訊息?
寫一個佇列,把 consumer 作為佇列類的一個屬性,然後增加一個消費計數的計數器,當到達指定數量時,關閉consumer。
25、Kafka 如何實現多執行緒的消費?
kafka 允許同組的多個 partition 被一個 consumer 消費,但不允許一個 partition 被同組的多個 consumer 消費。
實現多執行緒步驟如下:
生產者隨機分割槽提交資料(自定義隨機分割槽)。
消費者修改單執行緒模式為多執行緒,在消費方面得注意,得遍歷所有分割槽,否則還是隻消費了一個區。
26、 Kafka 消費支援幾種消費模式?
kafka消費訊息時支援三種模式:
at most once 模式 最多一次。保證每一條訊息 commit 成功之後,再進行消費處理。訊息可能會丟失,但不會重複。
at least once 模式 至少一次。保證每一條訊息處理成功之後,再進行commit。訊息不會丟失,但可能會重複。
exactly once 模式 精確傳遞一次。將 offset 作為唯一 id 與訊息同時處理,並且保證處理的原子性。訊息只會處理一次,不丟失也不會重複。但這種方式很難做到。
kafka 預設的模式是 at least once ,但這種模式可能會產生重複消費的問題,所以在業務邏輯必須做冪等設計。
在業務場景儲存資料時使用了 INSERT INTO ...ON DUPLICATE KEY UPDATE語法,不存在時插入,存在時更新,是天然支援冪等性的。
27、Kafka 如何保證資料的不重複和不丟失?
1、Exactly once 模式 精確傳遞一次。將 offset 作為唯一 id 與訊息同時處理,並且保證處理的原子性。訊息只會處理一次,不丟失也不會重複。但這種方式很難做到。
kafka 預設的模式是 at least once ,但這種模式可能會產生重複消費的問題,所以在業務邏輯必須做冪等設計。
2、冪等性:Producer在生產傳送訊息時,難免會重複傳送訊息。Producer進行retry時會產生重試機制,發生訊息重複傳送。而引入冪等性後,重複傳送只會生成一條有效的訊息。
具體實現:每個 Producer 在初始化時都會被分配一個唯一的 PID,這個 PID 對應用是透明的,完全沒有暴露給使用者。對於一個給定的 PID,sequence number 將會從0開始自增。Producer 在傳送資料時,將會給每條 msg 標識一個 sequence number,broker 也就是通過這個來驗證資料是否重複。這裡的 PID 是全域性唯一的,Producer 故障後重新啟動後會被分配一個新的 PID,這也是冪等性無法做到跨會話的一個原因。broker上每個Topic-Partition也會維護pid-seq的對映,並且每次 Commit 都會更新 lastSeq。這樣Record Batch 到來時,broker會先檢查 Record Batch 再儲存資料。如果batch中 baseSeq(第一條訊息的seq)比Broker維護的序號(lastSeq)大1,則儲存資料,否則不儲存。
3、使用 Exactly Once + 冪等,可以保證資料不重複,不丟失。
28、Kafka 是如何清理過期資料的?
kafka 將資料持久化到了硬碟上,允許你配置一定的策略對資料清理,清理的策略有兩個,刪除和壓縮。
資料清理的方式
1、刪除
log.cleanup.policy=delete 啟用刪除策略
直接刪除,刪除後的訊息不可恢復。可配置以下兩個策略:
#清理超過指定時間清理:
log.retention.hours=16
#超過指定大小後,刪除舊的訊息:
log.retention.bytes=1073741824
為了避免在刪除時阻塞讀操作,採用了 copy-on-write 形式的實現,刪除操作進行時,讀取操作的二分查詢功能實際是在一個靜態的快照副本上進行的,這類似於 Java 的 CopyOnWriteArrayList。
2、壓縮
將資料壓縮,只保留每個 key 最後一個版本的資料。
首先在 broker 的配置中設定 log.cleaner.enable=true 啟用 cleaner,這個預設是關閉的。
在 topic 的配置中設定 log.cleanup.policy=compact 啟用壓縮策略。