《Kafka權威指南》讀書筆記——生產者、消費者

一、Kafka基本概念

Kafka是分佈式日誌系統、分佈式流平臺,按序持久化,消費者按需讀取。Kafka 的數據單元被稱爲消息,可以理解爲數據庫的一行,爲了提高效率,消息被分批次寫入Kafka ,批次就是一組消息(通常我們所說的batch_size就是批次大小),需要在時間延遲和吞吐量之間做權衡,選擇合適的batch_size。消息寫入分區時用到,一般取鍵的hash,對分區數取模,保證相同鍵的消息落在同一分區上

Kafka的主題包含若干個分區,消息以追加的方式寫入分區,只能在分區維度保證消息有序性。Kafka通過分區來實現數據冗餘和伸縮性,即一個主題可以橫跨多個服務器。

生產者一般並不關心消息寫入哪個分區,會均衡的將消息分佈在主題的各個分區上,也可以通過鍵和分區器將消息指定到某一分區。

消費者通過偏移量消費消息,偏移量是另一種元數據,它是一個不斷遞增的整數值,在創建消息時, Kafka 會把它添加到消息裏,在給定的分區裏,每個消息的偏移量都是唯一的。消費者把每個分區最後讀取的消息偏移量保存在Zookeeper(舊版)或Kafka(新版)上舊版的並不具備再均衡和提交offset的可控性。如果悄費者關閉或重啓,它的讀取狀態不會丟失。消費者是消費者羣組的一部分,也就是說,會有一個或多個消費者共同讀取一個主題。消費組保證每個分區只能被一個消費者使用(如果不這麼做,需要額外的同步機制。如果需要發佈訂閱模式,應該爲每個消費者建立各自的消費組),一條消息只會被一個消費組消費一次(不考慮宕機)。消費者可以選擇提交offset的時間間隔,1 min比較合理,在 1 min內如果消息丟失可以重複讀取。

每個集羣都有一個broker 同時充當了集羣控制器的角色,控制器負責管理工作,包括將分區分配給broker 和監控broker。一個分區可以分配給多個broker,有一個broker作爲分區的首領,這種分區複製提供了消息冗餘,同時主分區應均爲分佈在各個broker上。Kafka可以保留消息,指定保留的時間長度,或者消息大小的閾值。

舊版的kafka將broker和主題的元數據、偏移量都保存在ZK上,新版的將偏移量保存在Kafka內部的_consume_offset分區。broker 會往擁有最少數目分區的路徑新增分區,而不是往擁有最小磁盤空間的路徑新增分區。

下面介紹一些主題的配置,根據經驗,把分區的大小限制在25GB 以內可以得到比較理想的效果,數據默認保存一週。如果同時指定了log.retention.bytes 和log.retention.ms,只要任意一個條件得到滿足,消息就會被刪除。log.segment.bytes和log.segment.ms可以指定日誌片段的大小和時間,達到閾值時關閉當前片段,打開新的片段進行寫入。只有當日志片段關閉時,纔會開始計算過期時間,所以最初到達的消息的存活時間可能會比設置的閾值還要大(過期應該按照批次的維度,否則過於麻煩)。message.max.bytes限制單個消息的大小。

 

二、生產者

(1)ProducerRecord包含目標主題和要發送的內容,還可以指定鍵或分區,再進行序列化。無鍵時輪詢選擇分區,有鍵hash。爲了節省網絡開銷,消息被添加到批次裏,批次按照(主題、分區)的維度來創建,被髮送到相同的主題和分區上。服務器返回RecordMetaData,包含主題、分區、偏移量信息,或者返回寫入失敗,生產者重試。生產者按分區組裝消息,按節點發送消息,最小化網絡開銷。

(2)生產者有三個必填屬性,broker和KV的序列化器。有三種消息發送方式,種方式。發送並忘記(fire- and-forget)
發送完井不關心消息是否正常到達,大多數情況下,消息會正常到達,因爲Kafka 是高可用的,而且生產者會自動嘗試重發。不過,使用這種方式有時候也會丟失一些消息;同步發送,使用send()方法發送消息,返回Future對象,get()判斷消息是否發送成功;異步發送,調用send(),指定一個回調函數, 服務器在返回響應時調用該函數。

如下是最簡單的fire-and-forget,指定主題和KV。

同步發送消息。

異步發送。

(3)生產者配置

acks,acks 參數指定了必須要有多少個分區副本收到消息,生產者纔會認爲消息寫入是成功的。一般設置爲0,1(只需主節點ack),all

buffer.memory指定內存緩衝區大小,compression.type指定壓縮算法。

retries指定最大重試次數,batch.size指定了批次大小,通常配合linger.ms(發送等待時間)使用。

max.in.flight.requests.per.connection指定了滑動窗口大小,此外還有獲取元數據、發送消息的超時時間,單個消息的大小閾值,TCP緩衝區大小等參數。

重試時,消息會亂序,如果要求嚴格有序,可以把滑動窗口大小設爲1

(4)序列化

介紹一下Avro 序列化,利用schema指定序列化策略,schema保存在註冊表裏。

(5)鍵可以作爲消息的附加信息,也可以用來決定消息該被寫到主題的哪個分區,如果鍵值爲null , 井且使用了默認的分區器,那麼記錄將被隨機地發送到主題內各個可用的分區上。分區器使用輪詢(Round Robin)將消息均衡地分佈到各個分區上。指定鍵的話則hash,只有在不改變主題分區數量的情況下,鍵與分區之間的映射才能保持不變

 

三、消費者

(1)Kafka 消費者從屬於消費者羣組。一個羣組裏的消費者訂閱的是同一個主題,每個消費者接收主題一部分分區的消息。一個分區只會被消費組裏的一個消費者消費。如果我們往羣組裏添加更多的消費者,超過主題的分區數量,那麼有一部分消費者就會被閒置,不會接收到任何消息。向消費組增加消費者是橫向伸縮的方式,不同的消費組或不影響

可以通過consumer.assign(partitions)手動分配分區,如果分區變化了cosumer不知情。

(2)再均衡

在增減消費組、分區時,會進行分區的再均衡。再均衡爲消費者羣組帶來了高可用性和伸縮性(我們可以放心地添加或移除消費者)。但是再均衡期間,消費者無法讀取消息,造成整個羣組一小段時間的不可用。另外,當分區被重新分配給另一個消費者時,消費者當前的讀取狀態(offset)會丟失,還需要刷新緩存。消費者通過向被指派爲羣組協調器的broker(不同的羣組可以有不同的協調器)發送心跳來維持它們和羣組的從屬關係以及它們對分區的所有權關係,停止心跳時間達到閾值則認爲消費者死亡,進行再均衡。新版本的Kafka引入了心跳線程,將輪詢和發送心跳獨立開來,防止輪詢時間過長,被判斷爲死亡。

當消費者要加入羣組時,它會向羣組協調器發送一個Join Group請求。第一個加入羣組的消費者將成爲“羣主”。創建消費者時,需要指定broker,KV序列化方式和消費組(非必須,可以不指定,不過不常見)。subscribe()訂閱主題,也可以傳入正則表達式。

(3)輪詢

如下是消費者輪詢,poll()指定超時時間,返回一個記錄列表。每條記錄都包含了記錄的主題、分區、偏移量,和鍵值對。輪詢不只是獲取數據那麼簡單。在第一次調用新消費者的poll(),它會負責查找GroupCoordinator,然後加入羣組,接受分配的分區。如果發生了再均衡,整個過程也是在輪詢期間進行的。當然,心跳也是從輪詢裏發送出去的。所以,我們要確保在輪詢儘快完成。在同一個羣組裏,我們無法讓一個線程運行多個消費者,也無法讓多個線程安全地共享一個消費者。按照規則,一個消費者使用一個線程

(4)消費者配置

fetch.min.bytes指定了消費者獲取記錄的最小字節數,可用數據達到該值時纔會發送給消費者,fetch.max.wait.ms指定等待時間,和fetch.min.bytes配合使用,看哪個優先滿足。

max. parition.fetch.bytes指定了服務器從每個分區裏返回給消費者的最大字節數,默認1 MB

session.timeout.ms指定心跳超時時間,默認3s 。heartbeat.interval.ms指定poll()向協調器發送心跳的頻率,一般需要同時修改這兩個屬性,heartbeat.interval.ms必須比session.timeout.ms小,一般是session.timeout.ms的三分之一。

auto.offset.reset指定了消費者在讀取一個沒有偏移量的分區或者偏移量時如何處理。它的默認值是latest ,從最新的記錄開始讀取數據。另一個值是earliest ,從起始位置讀取分區的記錄。

enable.auto.commit指定了消費者是否自動提交偏移量,默認true。爲了儘量避免出現重複數據和數據丟失,可以把它設爲false ,由自己控制何時提交偏移量。如果把它設爲true,還可以通過配置auto.commit.interval.ms來控制提交的頻率。

partition.assignment.strategy指定分區策略,Range分配連續分區,RoundRobin輪詢分配,默認Range。

max.poll.records控制單次調用poll() 能夠返回的記錄數量,可以幫你控制在輪詢裏需要處理的數據量。

此外,還可以標誌消息,指定TCP緩衝區大小。

(5)提交偏移量

消費者追蹤消息在分區裏的位置,並向主題_consumer_offset提交偏移量(或者ZK),再均衡之後,消費者可能分配到新的分區,拉取偏移量繼續處理。偏移量的提交要在效率和消息丟失之間做權衡。如果enable.auto.commit = true,消費者會自動把從poll()接收到的最大偏移量提交上去,提交時間間隔由auto.commit.interval.ms控制,默認值是5s。自動提交雖然方便, 不過並沒有爲開發者留有餘地來避免重複處理消息。

enable.auto.commit = false時,可以commitSync()或者commitAsync()手動提交。可以組合使用同步和異步提交,在try塊裏異步提交,在finally塊裏同步提交,應對異常情況。消費者API允許在commitSync()或者commitAsync()裏傳進去希望提交的分區和偏移量的map,指定需要提交的偏移量。

可以使用seekToBeginning和seekToEnd方法從指定的偏移量讀取消息。如果應用程序從Kafka讀取數據、處理、入庫,不想重複入庫,可以將偏移量和處理過的記錄原子入庫,下次查找偏移量,seek()讀取數據。

(6)再均衡監聽器

在調用subscribe() 時傳入ConsumeRebalancelistener,監聽再均衡。

(7)如果想要退出輪詢,需要通過另一個線程調用consumer.wakeup,是消費者唯一一個可以從其他線程裏安全調用的方法,可以退出poll(),並拋出wakeupException。

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