Kafka源碼解析與實戰

Kafka的架構

包括Kafka的基本組成,Kafka的拓撲結構以及Kafka的內部通信協議。Kafka內部的通信協議是建立在Kafka的拓撲結構之上,而Kafka的拓撲結構是由Kafka的基本模塊所組成的。
AK RELEASE 2.5.0
APRIL 15, 2020

Kafka的基本組成

Kafka集羣中生產者將消息發送給以Topic命名的消息隊列Queue中,消費者訂閱發往以某個Topic命名的消息隊列Queue中的消息。其中Kafka集羣由若干個Broker組成,Topic由若干個Partition組成,每個Partition裏的消息通過Offset來獲取。
基本組成包括:

  • Broker:一臺Kafka服務器就是一個Broker,一個集羣由多個Broker組成,一個Broker可以容納多個Topic,Broker和Broker之間沒有Master和Standby的概念,他們之間地位是平等的
  • Topic:每條發送到Kafka集羣的消息都屬於某個主題,這個主題就稱爲Topic。物理上不同Topic的消息分開存儲,邏輯上一個Topic的消息雖然保存在一個或多個Broker上,但是用戶只需指定消息主題Topic即可生產或消費數據而不需要關心數據存放在何處。
  • Partition:爲了實現可擴展性,一個非常大的Topic可以被分爲多個Partition,從而分佈到多臺Broker上。Partition中的每條消息都會被分配一個自增Id(Offset)。Kafka只保證按照一個Partition中的順序將消息發送給消費者,但是不保證單個Topic中多個Partition之間的順序。
  • Offset:消息在Topic的Partition中的位置,同一個Partition中的消息隨着消息的寫入其對應的Offset也自增。
  • Replica:副本,Topic的Partition有N個副本,N爲副本因子。其中一個Replica爲Leader,其他都爲Follower,Leader處理Partition的所有讀寫請求,Follower定期同步Leader上的數據。
  • Message:消息是通信的基本單位。每個Producer可以向一個Topic發佈消息
  • Producer:消息生產者,將消息發佈到指定的Topic中,也能夠決定消息所屬的Partition:比如基於Round-Robin或者Hash算法
  • Consumer:消息消費者,向指定的Topic獲取消息,根據指定Topic的分區索引及其對應分區上的消息偏移量來獲取消息
  • Consumer Group:消費者組,每個消費者都屬於一個組。當消費者具有相同組時,消息會在消費者之間負載均衡。一個Partition的消息只會被相同消費者組中的某個消費者消費。不同消費者組是相互獨立的。
  • Zookeeper:存放Kafka集羣相關元數據的組件。Zookeeper集羣中保存了Topic的狀態信息,例如分區個數、分區組成、分區的分佈情況等;保存Broker的狀態信息;保存消費者的消費信息等。通過這些信息,Kafka很好地將消息生產、消息存儲、消息消費的過程結合起來。

Kafka的拓撲結構

一個典型的Kafka集羣中包含若干個Producer(可以是某個模塊下發的Command,或者是Web前端產生的PageView,或者是服務器日誌,系統CPU、Memory等),若干個Broker(Kafka集羣支持水平擴展,一般Broker數量越多,整個Kafka集羣的吞吐率也就越高),若干個Consumer Group,以及一個Zookeeper集羣。Kafka通過Zookeeper管理集羣配置。Producer使用Push模式將消息發佈到Broker上,Consumer使用Pull模式從Broker上訂閱並消費消息。

簡單的消息發送流程如下:

  1. Producer根據指定的路由方法,將消息Push到Topic的某個Partition裏
  2. Kafka集羣接收到Producer發過來的消息並持久化到硬盤,並保留消息指定時長(可配置),不關注消息是否被消費。
  3. Consumer從Kafka集羣Pull數據,並控制獲取消息的Offset

Kafka內部的通信協議

Kafka內部各個Broker之間的角色並不是完全相等的,Broker內部負責管理分區和副本狀態以及異常情況下分區的重新分片等這些功能的模塊稱爲KafkaController。每個Kafka集羣中有且只有一個Leader狀態的KafkaController,當其出現異常時,其餘Standby狀態的KafkaController會通過Zookeeper選舉出有一個Leader狀態的KafkaController。

維度一:通信協議詳情

  • ProducerRequest:生產者發送消息的請求,生產者將消息發送至Kafka集羣中的某個Broker,Broker接收到此請求後持久化此消息並更新相關元數據信息。
    ProducerRequest.requiredAcks的取值爲0時,生產者不關心Broker Server端持久化執行結果,但是高級消費者發送的提交偏移量的請求還是需要返回具體執行結果。爲1則生產者消費者都需要將Broker Server端持久化的執行結果返回客戶端。爲-1時,不會立刻返回Broker Server端消息持久化的結果,而是需要等待Partition的ISR列表中的Replica完成數據同步,並且ISR列表的個數大於min.insync.replicas時纔會將響應返回給對應客戶端。這裏採用的是稱爲Purgatory的策略。Broker Server上對應的Partition的HighWatermark發生改變才觸發檢查。
  • TopicMetadataRequest:獲取Topic元數據信息的請求,無論是生產者還是消費者都需要通過此請求來獲取感興趣的Topic的元數據。
  • FetchRequest:消費者獲取Topic的某個分區的消息的請求。分區狀態爲Follower的副本也需要利用此請求去同步分區狀態爲Leader的對應副本數據。
  • OffsetRequest:消費者發送至Kafka集羣來獲取感興趣Topic的分區偏移量的請求,通過此請求可以獲知當前Topic所有分區在不同時間段的偏移量詳情。
  • OffsetCommitRequest:消費者提交Topic被消費的分區偏移量信息至Broker,Broker接收到此請求後持久化相關偏移量信息。
  • OffsetFetchRequest:消費者發送獲取提交至Kafka集羣的相關Topic被消費詳細信息,和OffsetCommitRequest相互對應。
  • LeaderAndIsrRequest:當Topic的某個分區狀態發送變化時,處於Leader狀態的KafkaController發送至相關Broker通知其做出相應處理。
    當某個Replica稱爲Leader:暫停Fetch→添加進Assigned Replica列表→添加進In-Sync Replica列表→刪除已經不存在的Assigned Replica→初始化Leader Replica的HighWatermark
    當某個Replica成爲Follower:暫停舊的Fetch線程→截斷數據至HighWatermark以下→開啓新的Fetch線程→添加進Assigned Replica列表→刪除已經不存在的Assigned Replica
  • StopReplicaRequest:當Topic的某個分區被刪除或者下線的時候,處於Leader狀態KafkaController發送至相關Broker通知其做出相應處理。
  • UpdateMetadataRequest:當Topic的元數據信息發生變化時,處於Leader狀態的KafkaController發送至相關Broker通知其做出相應處理。
  • BrokerControllerShutdownRequest:當Broker正常下線時,發送此請求到處於Leader狀態的KafkaController。
  • ConsumerMetadataRequest:獲取保存特定Consumer Group消費詳情的分區信息

維度二:通信協議交互

  • Producer和Kafka集羣:Producer需要利用ProducerRequest和TopicMetadataRequest來完成Topic元數據的查詢、消息的發送。
  • Consumer和Kafka集羣:Consumer需要利用TopicMetadataRequest請求、FetchRequest請求、OffsetRequest、OffsetCommitRequest、OffsetFetchRequest、ConsumerMetadataRequest來完成Topic元數據的查詢、消息的訂閱、歷史偏移量的查詢、偏移量的提交、當前偏移量的查詢。
  • KafkaController狀態爲Leader的Broker和KafkaController狀態爲Standby的Broker:Leader需要用LeaderAndIsrRequest、StopReplicaRequest、UpdateMetadataRequest來完成對Topic的管理。Standby需要利用BrokerControllerShutdownRequest來通知Leader自己的下線動作。
  • Broker和Broker之間:Broker相互之間需要利用FetchRequest請求來同步Topic分區的副本數據,這樣才能使Topic分區各副本數據保持一致。

Broker概述

Broker內部存在的功能模塊包括SocketServer、KafkaRequestHandlerPool、LogManager、ReplicaManager、OffsetManager、KafkaScheduler、KafkaApis、KafkaHealthcheck和TopicConfigManager九大基本模塊以及KafkaController集羣控制管理模塊。

  • SocketServer:首先開啓一個Acceptor線程,新的Socket連接成功建立時會將對應的SocketChannel以輪詢方式轉發給N個Processor線程中的某一個,由其處理接下來SocketChannel的請求,將請求放置在RequestChannel中的請求隊列;當Processor線程監聽到SocketChannel請求的響應時,會將響應從RequestChannel中的響應隊列中取出來併發給客戶端
  • KafkaRequestHandlerPool:真正處理Socket請求的線程池,其個數默認爲8個,由參數num.io.threads決定。該線程池裏面的線程KafkaRequestHandler從RequestChannel的請求隊列中獲取Socket請求,然後調用KafkaApis完成真正業務邏輯最後將響應寫回至RequestChannel中的響應隊列,並交給SocketServer中對應的Processer線程發給客戶端。
  • LogManager:Kafka的日誌管理模塊,主要提供針刪除任何過期數據和冗餘數據,刷新髒數據,對日誌文件進行Checkpoint以及日誌合併的功能。
    負責提供Broker Server上Topic的分區數據讀取和寫入功能,負責讀取和寫入位於Broker Server上的所有分區副本數據。如果Partition有多個Replica,則每個Broker Server不會存在相同Partition的Replica,如果存在一旦遇到Broker Server下線會丟失Partition的多份副本可用性降低。
    LogManager中包含多個TopicAndPartition,每個TopicAndPartition對應一個Log,每個Log中包含多個LogSegment(每個LogSegment文件包括一個日誌數據文件【.log】和兩個索引文件(偏移量索引文件【.index】和消息時間戳索引文件【.timeindex】))。LogSegment的結構中log代表消息集合,每條消息都有一個Offset,這是針對Partition中的偏移量;index代表的是消息的索引信息,以KV對的形式記錄,其中K爲消息在log中的相對偏移量,V爲消息在log中的絕對位置;baseOffset代表的是該LogSegment日誌段的起始偏移量;indexIntervalByte代表的索引粒度,即寫入多少字節之後生成一條索引。OffsetIndex不會保存每條消息的索引,因此其索引文件是一個稀疏索引文件(稀疏索引:索引項中只對應主文件中的部分記錄,即不會給每條記錄建立索引)。

後臺還會維護一個日誌合併線程,Kafka發送消息的時候需要攜帶3個參數(Topic,Key,Message),針對相同的Key值不同的Message只保留最後一個Key值對應的消息內容。

  • ReplicaManager:Kafka副本管理模塊。主要提供針對Topic分區副本數據的管理功能,包括有關副本的Leader和ISR的狀態變化、副本的刪除、副本的監測等。ISR全稱是In-Syn Replicas,處於同步狀態的副本。AR全稱是Assign Replicas的縮寫,代表分配給Partition的副本。
    主要利用ReplicaFetcherThread(副本數據拉取線程)和High Watermark Mechanism(高水位線機制)來實現數據的同步管理。單個ReplicaFetcherThread線程負責某個Broker Server上部分TopicAndPartition的Replica數據同步。將拉取的消息寫入log,更新當前Replica的HighWatermark,代表的是ISR中所有replicas的last commited message的最小起始偏移量。當某個Broker Server上被分配到Replica的時候會進入becomeLeaderOrFollower處理流程;當Replica被刪除爲進入stopReplicas處理流程;當Follower狀態的Replica長時間沒有同步Leader狀態的Replica的時候會進入maybeShrinkIsr處理流程。
  • OffsetManager:Kafka的偏移量管理模塊,主要提供針對偏移量的保存讀取的功能,兩種方式保存:一種是把偏移量保存到Zookeeper上,另一種是Kafka,把偏移量提交至Kafka內部Topic爲"__consumer_offsets"的日誌裏面,主要由offsets.storage參數決定。默認爲Zookeeper。
  • KafkaScheduler:Kafka的後臺任務調度資源池。提供後臺定時任務的調度,主要爲LogManager、ReplicaManager、OffsetManager提供調度服務。
  • KafkaApis:Kafka的業務邏輯實現層,根據不同的Request執行不同的操作,其中利用LogManager、ReplicaManager、OffsetManager來完成內部處理。
  • KafkaHealthcheck:Broker Server在./brokers/ids上註冊自己的ID,當Broker在線的時候,則對應的ID存在;當離線時對應ID不存在,從而達到集羣狀態監測的目的。
  • TopicConfigManager:在/config/changes上註冊自己的回調函數來監測Topic配置信息的變化
  • KafkaController:Kafka集羣控制管理模塊。由於Zookeeper上保存了Kafka機器的元數據信息,因爲KafkaController通過在不同目錄註冊不同的回調函數來達到監測集羣狀態的目的,以及響應集羣狀態的變化:
    1. /controller目錄保存了Kafka集羣中狀態爲Leader的KafkaController標識,通過監測這個目錄的變化可以即使響應KafkaController狀態的切換
    2. /admin/reassign_partitions目錄保存了Topic重分區的信息,通過監測這個目錄的變化可以及時響應Topic分區變化的請求
    3. /admin/preferred_replica_election目錄保存了Topic分區副本的信息,通過監測這個目錄的變化可以及時響應Topic分區副本變化的請求。
    4. /brokers/topics目錄保存了Topic的信息,通過監測這個目錄的變化可以及時響應Topic創建和刪除的請求。
    5. /brokers/ids目錄保存了Broker的狀態,通過監測這個目錄的變化可以及時響應Broker的上下線情況。

Broker的控制管理模塊

每個Broker內部都會存在一個KafkaController模塊,但是有且只有一個Broker內部的KafkaController模塊對外提供控制管理Kafka集羣的功能,例如負責Topic的創建、分區的重分配以及分區副本Leader的重新選舉等。

KafkaController的選舉策略

Leader和Follower的選舉是基於Zookeeper實現的,嘗試在Zookeeper的相同路徑上創建瞬時節點(Ephemeral Node),只有一個KafkaController會創建成功。其中負責狀態管理的類爲ZookeeperLeaderElector,字面意思上就可以看出是基於Zookeeper的Leader選舉權。其中包含了controllerContext當前Topic的元數據信息以及集羣的元數據信息等;electionPath爲多個KafkaController競爭寫的路徑,其值爲/controller;onBecomingLeader爲狀態轉化成Leader時候的回調函數;onResigningAsLeader爲狀態轉化位Follower時候的回調函數;brokerId爲當前Broker Server的Id。ZookeeperLeaderElector啓動後負責觀察數據節點狀態,瞬時節點消失觸發再次選舉,嘗試寫入的節點內容就是brokerId。

KafkaController的初始化

當選舉爲Leader時分爲下面幾步:

  1. 初始化Kafka集羣內部的時鐘,存放在Zookeeper的/controller_epoch,Broker Server用這個值區分請求的時效性
  2. 註冊各種監聽函數,針對Zookeeper不同目錄下Kafka存儲的不同元數據進行監聽。
  3. 通過initializeControllerContext()、replicaStateMachine.startup()和partitionStateMachine.startup()初始化Kafka集羣內部元數據信息。建立和集羣內其他狀態爲Follower的KafkaController的通信鏈路,處理集羣啓動前沒有及時處理的用戶請求,此時可能會變更Kafka集羣內部的元數據信息,最後通過sendUpdateMetadataRequest()將Kafka集羣內部的元數據信息同步給其他狀態爲Follower的KafkaController。
  4. 根據auto.leader.rebalance.enable配置項按需啓動Kafka集羣內部的負載均衡線程,默認開啓
  5. 根據delete.topic.enable配置項按需啓動Kafka集羣內部的Topic刪除線程,默認關閉

選舉爲Follower的時候分爲下面幾步,正好與Controller相反:

  1. 取消Zookeeper路徑上的監聽函數
  2. 根據delete.topic.enable配置項按需啓動Kafka集羣內部的Topic刪除線程,默認關閉
  3. 關閉Kafka集羣內部的負載均衡線程
  4. 斷開和集羣內其他狀態爲Follower的KafkaController的通信線路
  5. 重置集羣內部時鐘

Topic的分區狀態轉化機制

Topic的分區狀態維護是由PartitionStateMachine模塊負責的,通過在/brokers/topics 和 /admin/delete_topics目錄上註冊不同的監聽函數,監聽Topic的創建和刪除事件觸發Topic分區狀態的轉換。
PartitionStateMachine中分區狀態由PartitionState用一個字節表示不同狀態,分爲四種:

  • NonExistentPartition:代表分區從來沒有被創建或者被創建之後又被刪除呃狀態
  • NewPartition:分區剛創建包含AR,但是此時Leader或ISR還沒有創建,處於非活動狀態無法接收數據
  • OnlinePartition:分區Leader已經被選舉,產生來對應的ISR,處於活動狀態可以接收數據
  • OfflinePartition:代表分區Leader由於某種原因下線時導致分區暫時不可用

每個狀態都是由一個合理的前置狀態轉換而來。

Topic分區的領導者副本選舉策略

Topic分區的Leader Replica在不同場景下的選舉策略是不一樣的,不同選舉策略都基礎PartitionLeaderSelector。其根據Topic、Partition、當前Leader、當前的ISR選舉出新的Leader,新的ISR和新的AR(在線狀態),共有5種不同的策略:

  • NoOpLeaderSelector:默認的選舉策略
  • ReassignedPartitionLeaderSelector:當分區AR重新分配時使用的策略
  • PreferredReplicaPartitionLeaderSelector:集羣內部自動平衡負載或者用戶觸發手動平衡負載時使用的策略
    隨着Topic的新建刪除以及Broker Server的上下線,原本Topic分區的Leader Replica在集羣中的分佈越來越不均勻。 auto.leader.rebalance.enable爲true,則會自動觸發分區的Leader Replica選舉,或者管理員下發分區Leader Replica選舉指令。這會在Zookeeper的 /admin/preferred_replica_election指定具體的Topic和分區,此時Leader狀態的KafkaController監測到這個路徑的數據變化就會觸發相應的回調函數,促使對應的Topic分區發生Leader Replica的選舉。
  • OfflinePartitionLeaderSelector:分區狀態從OfflinePartition或者NewPartition切換爲OnlinePartition時使用的策略
  1. 篩選出在線的ISR和AR
  2. 優先從在線的ISR中選擇,如果列表不爲空則選擇列表中的第一個,選舉結束
  3. 在線ISR爲空,根據 unclean.leader.election.enable 決定是否從在線的AR中選舉Leader,如果允許,則選擇AR列表中的第一個,結束選舉,如果AR列表爲空選舉失敗。
  • ControllerShutdownLeaderSelector:Leader狀態的KafkaController處理其他Broker Server下線導致分區的Leader Replica發生切換時使用的策略。
  1. 篩選出在線的ISR
  2. 剔除離線的ISR形成新的ISR列表
  3. 如果新的ISR列表不爲空,則選舉第一個Replica作爲新的Leader,否則選舉失敗

Topic分區的副本狀態轉換機制

Topic分區的副本狀態維護是由ReplicaStateMachine模塊負責的,Topic分區的副本狀態伴隨着Topic分區狀態的變化而變化
分區副本狀態只要有7種:

  • NewReplica:分區剛被分配但是沒有開始工作的狀態
  • OnlineReplica:分區副本開始工作時的狀態,此時該副本時該分區的Leader或者Follower
  • OfflineReplica:分區副本所在的Broker Server宕機時所導致的副本狀態
  • ReplicaDeletionStarted:分區副本下線之後準備開始刪除的狀態
  • ReplicaDeletionSuccessful:相關Broker Server正確響應分區副本被刪除請求之後的狀態
  • ReplicaDeletionIneligible:相關Broker Server錯誤響應分區副本被刪除請求之後的狀態
  • NonExistentReplica:代表分區副本被徹底刪除之後的狀態

目標狀態也是由合理的前置狀態轉換而來的。

KafkaController內部的監聽器

KafkaController內部通過監聽函數來維護集羣的元數據。

  • TopicChangeListener:註冊在 /broker/topics 路徑,監聽Topic的創建
  • AddPartitionListener:在Topic創建過程中會在 /broker/topics/[topic]目錄下注冊AddPartitionListener用於監聽Topic分區的變化
  • PartitionReassignedListener:KafkaController轉換爲Leader的過程中在路徑 /admin/reassign_partitions註冊了PartitionReassignedListener用於監聽Topic分區的重分配。在正式啓動重分配之前會判斷是否需要進行重分配,重分配之後的AR列表和當前的AR列表不相同並且重分配之後的AR列表所在的Broker Server都在線,滿足上面兩個條件纔會觸發分區的重分配。
  • ReassignedPartitionsIsrChangeListener:當Leader Replica所在的Broker Server接收到來自Follower Replica的FetchRequest請求時,KafkaApis的handleFetchRequest會統計每個Replica的狀態,一旦發現改Replica同步上Leader Replica之後,此時會調用Partition的updateLeaderHWAndMaybeExpandIsr及時更新 /brokers/topics/[topic]/partitions/partitionId/state/ 目錄上的Partition狀態,包括Leader、ISR等信息,監聽到分區狀態發生變化會觸發ReassignedPartitionsIsrChangeListener
  • PreferredReplicaElectionListener:每個Partition可以有多個Replica,即AR列表。在這個列表中的第一個Replica稱爲“Preferred Replica”,當創建Topic時,Kafka要確保所有的Topic的“Preferred Replica”均勻地分佈在Kafka集羣中。Topic的Partition需要重新均衡Leader Replica至Preferred Replica,此時會觸發PreferredReplicaElectionListener
  • BrokerChangeListener:Broker Server的上下線影響着其中所有Replica的狀態,因此ReplicaStateMachine在路徑爲/broker/topic的路徑上註冊了BrokerChangeListener,用於監聽Broker Server的上下線。
    Broker Server上線時步驟爲:
  1. Leader狀態的KafkaController同步Kafka集羣所有的Topic信息給新上線的Broker Server。
  2. 將原本位於該Broker Server上的所有Replica狀態切換至OnlineReplica
  3. 如果Partition的Replica有且只有一個,並且正好位於Broker Server上,則切換Partition狀態至OnlinePartition。
  4. 如果分區重分配之前由於該Broker Server下線導致推出的話,則嘗試重新進行分區重分配。
  5. 如果之前由於Broker Server下線導致對應的Replica無法刪除的話,則恢復刪除流程。
    Broker Server下線步驟:
  6. 更新ControllerContext內部正在下線的Broker Server列表
  7. 將Leader Replica位於該Broker Server上的分區狀態切換爲OnlinePartition,緊接着觸發分區狀態切換爲OnlinePartition,利用OfflinePartitionLeaderSelector副本選舉策略進行Leader Replica的選舉。
  8. 將位於該Broker Server上的Replica狀態切換爲OfflineReplica
  9. 如果對應Replica的Topic處於刪除隊列中的話,則標記暫時無法刪除。
  • DeleteTopicsListener:監聽Topic的刪除

Kafka集羣的負載均衡流程

Partition的AR列表的第一個Replica稱爲“Preferred Replica”,並均勻分佈在整個Kafka集羣中。由於每個Partition只有Leader Replica對外提供讀寫服務,並且Partition創建的時候默認的Leader Replica位於Preferred Replica之上,此時Kafka集羣的負載是均衡的,如果Kafka集羣長時間運行,Broker Server中途由於異常而發生重啓,此時Partition的Leader Replica會發生遷移,這樣會導致其Partition的Leader Replica在集羣中不再均衡了。

Kafka集羣的Topic刪除流程

Topic是由Partition組成的,而Partition是由Replica組成的,因此只有Partition的Assigned Replica全部被刪除了該Partition纔可以被刪除;只有Topic的所有Partition都被刪除了,該Topic纔可以最終真正的被刪除。

KafkaController的通信模塊

ControllerChannelManager提供了Leader狀態的KafkaController和集羣其他Broker Server通信的功能,內部針對每一個在線的Broker Server會維護一個通信鏈路,並分別通過各自的RequestSendThread線程將請求發送給對應的Broker Server。

Topic管理工具

kafka-topics.sh提供了Topic的創建、修改、列舉、描述、刪除功能,在內部時通過TopicCommand類來實現的。
kafka-reassign-partitions.sh提供來重新分配分區副本的能力。該工具可以促進Kafka集羣的負載均衡。因爲Follower Replica需要從Leader Replica Fetch數據以保持與與Leader Replica同步,僅保持Leader Replica分佈的平衡對整個集羣的負載均衡時不夠的。另外當Kafka集羣擴容後,該工具可以將已有Topic的Partition遷移到新加入的Broker上。
分區重分片是一個異步的流程,因此該腳本還提供了查看當前分區重分配進度的指令。
kafka-preferred-replica-election.sh用於在整個集羣中恢復Leader Replica爲Preferred Replica。

生產者

生產者是指消息的生成者。生產者可以通過特定的分區函數決定消息路由到Topic的某個分區。消息的生產者發送消息有兩種模式,分別爲同步模式和異步模式。
kafka.javaapi.producer.Producer#send方法發送
指定 metadata.broker.list 屬性,配置Broker地址
指定 partitioner.class 屬性,配置分區函數,分區函數決定路由。分區函數必須實現 kafka.producer.Partitioner的 partition接口,參數爲消息key值,分區總數,返回值爲分區的索引。

Producer內部包括以下幾個主要模塊:

  • ProducerSendThread:當producer.type配置爲async,則其主要用於緩存客戶端的KeyedMessage,然後累積到batch.num.messages配置數量或者間隔 queue.enqueue.timeout.ms配置的時間還沒有獲取到新的客戶端的KeyedMessage,則調用DefaultEventHandler將KeyedMessage發送出去
  • ProducerPool:緩存客戶端和各個Broker Server的通信,DefaultEventHandler從ProducerPool中獲取和某個Broker Server的通信對象SyncProducer,然後通過SyncProducer將KeyedMessage發送給指定的Broker Server。
  • DefaultEventHandler:將KeyedMessage集合按照分區規則計算不同Broker Server所應該接收的部分KeyedMessage,然後通過SyncProducer將KeyedMessage發送出去。在DefaultEventHandler模塊內部提供來SyncProducer發送失敗的重試機制和平滑擴容Broker Server的機制。

發送模式

生產者由兩種發送模式:同步和異步
當producer.type配置爲sync時,同步發送消息。
當producer.type配置爲async時,異步發送消息。

消費者

Kafka提供了兩種不同的方式來獲取消息:簡單消費者和高級消費者。簡單消費者獲取消息時,用戶需要知道待消費的消息位於哪個Topic的哪個分區,並且該目的分區的Leader Replica位於哪個Broker Server上;高級消費者獲取消息時,只需要指定待消費的消息屬於哪個Topic即可。

簡單消費者

簡單消費者提供的客戶端API稱爲低級API,本質上客戶端獲取消息最終時利用FetchRequest請求從目的端Broker Server拉取消息。
FetchRequest請求中可以指定Topic的名稱,Topic的分區,起始偏移量、最大字節數。
客戶端無論生產消息還是消費消息,最終都是通過與目的地端Broker Server建立通信鏈路,並且以阻塞模式允許,然後通過該條鏈路將不同的請求發送出去。

高級消費者

高級消費者以Consumer Group(消費組)的形式來管理消息的消費,以Stream(流)的形式來提供具體消息的讀取。Stream是指來自若干個Broker Server上的若干個Partition的消息。客戶端需要正確設置Stream的個數,並且應該針對每個Stream開啓一個線程進行消息的消費。一個Stream代表了多個Partition消息的聚合,但是每一個Partition只能映射到一個Stream。
消息的最終獲取是通過遍歷KafkaStream的迭代器ConsumerIterator來逐條獲取的,其數據來源於分配給該KafkaStream的阻塞消息隊列BlockingQueue,而BlockingQueue的數據來源針對每個Broker Server的FetchThread線程。FetchThread線程會將Broker Server上的部分Partition數據發送給對應的阻塞消息隊列BlockingQueue,而KafkaStream正是從該阻塞消息隊列BlockingQueue中不斷的消費消息。
ConsumerThread本質上是客戶端的消費線程,消費若干個Partition上的數據,並且與BlockingQueueu相互映射,只要確定了ConsumerThread和Partition之間的關係,也就確定了BlockingQueue和Partition之間的關係。Kafka提供了兩種ConsumerThread和Partition的分配算法Range(範圍分區分配)和RoundRobin(循環分區分配)
高級消費者中,每個具體消費者實例啓動之後會在/consumers/[group]/ids/的Zookeeper目錄下注冊自己的id;Kafka集羣內部Topic會在/brokers/topics/[topic]/的Zookeeper目錄下注冊自己的Partition,因此消費者實例一旦發現以上2個路徑的數據發生變化時,則會觸發高級消費者的負載均衡流程,除此之外,消費者實例一旦和Zookeeper的鏈接重新建立時也會觸發高級消費者的負載均衡流程。
高級消費者內部針對Zookeeper的連接建立、Topic的Partition變化、Consumer的新增會建立3個不同的Listener,分別是ZKSessionExpireListener、ZKTopicPartitionChangeListener和ZKRebalancerListener。
高級消費者消費消息時提供了兩種持久化偏移量的機制,由參數auto.commit.enable,默認爲true自動提交。否則需要手動調用ZookeeperConsumerConnector的commitOffsets。Kafka根據參數offsets.storage,默認爲zookeeper(保存路徑爲/consumers/[group]/offset/[topic]/[partition]),可以設置爲kafka(保存再Topic爲“__consumer_offsets”的日誌中)。高級消費者內部會自動間隔一定時間(由參數 auto.commit.interval.ms決定,默認60*1000ms)

面試相關

kafka高吞吐量的原因:

一、順序讀寫磁盤,充分利用了操作系統的預讀機制。
二、linux中使用sendfile命令,減少一次數據拷貝,如下。
①把數據從硬盤讀取到內核中的頁緩存。
②把數據從內核中讀取到用戶空間。(sendfile命令將跳過此步驟)
③把用戶空間中的數據寫到socket緩衝區中。
④操作系統將數據從socket緩衝區中複製到網卡緩衝區,以便將數據經網絡發出
三、生產者客戶端緩存消息批量發送,消費者批量從broker獲取消息,減少網絡io次數,充分利用磁盤順序讀寫的性能。
四、通常情況下kafka的瓶頸不是cpu或者磁盤,而是網絡帶寬,所以生產者可以對數據進行壓縮。

kafka在高併發的情況下,如何避免消息丟失和消息重複?

消息丟失解決方案:
首先對kafka進行限速, 其次啓用重試機制,重試間隔時間設置長一些,最後Kafka設置acks=all,即需要相應的所有處於ISR的分區都確認收到該消息後,纔算發送成功
消息重複解決方案:
消息可以使用唯一id標識
生產者(ack=all 代表至少成功發送一次)
消費者 (offset手動提交,業務邏輯成功處理後,提交offset)
落表(主鍵或者唯一索引的方式,避免重複數據)
業務邏輯處理(選擇唯一主鍵存儲到Redis或者mongdb中,先查詢是否存在,若存在則不處理;若不存在,先插入Redis或Mongdb,再進行業務邏輯處理)

kafka怎麼保證數據消費一次且僅消費一次

冪等producer:保證發送單個分區的消息只會發送一次,不會出現重複消息
事務(transaction):保證原子性地寫入到多個分區,即寫入到多個分區的消息要麼全部成功,要麼全部回滾流處理EOS:流處理本質上可看成是“讀取-處理-寫入”的管道。此EOS保證整個過程的操作是原子性。注意,這隻適用於Kafka Streams

kafka保證數據一致性和可靠性

數據一致性保證

一致性定義:若某條消息對client可見,那麼即使Leader掛了,在新Leader上數據依然可以被讀到
HW-HighWaterMark: client可以從Leader讀到的最大msg offset,即對外可見的最大offset, HW=max(replica.offset)
對於Leader新收到的msg,client不能立刻消費,Leader會等待該消息被所有ISR中的replica同步後,更新HW,此時該消息才能被client消費,這樣就保證瞭如果Leader fail,該消息仍然可以從新選舉的Leader中獲取。
對於來自內部Broker的讀取請求,沒有HW的限制。同時,Follower也會維護一份自己的HW,Folloer.HW = min(Leader.HW, Follower.offset)

數據可靠性保證

當Producer向Leader發送數據時,可以通過acks參數設置數據可靠性的級別
0: 不論寫入是否成功,server不需要給Producer發送Response,如果發生異常,server會終止連接,觸發Producer更新meta數據;
1: Leader寫入成功後即發送Response,此種情況如果Leader fail,會丟失數據
-1: 等待所有ISR接收到消息後再給Producer發送Response,這是最強保證

kafka到spark streaming怎麼保證數據完整性,怎麼保證數據不重複消費?

保證數據不丟失(at-least)

spark RDD內部機制可以保證數據at-least語義。
Receiver方式
開啓WAL(預寫日誌),將從kafka中接受到的數據寫入到日誌文件中,所有數據從失敗中可恢復。
Direct方式
依靠checkpoint機制來保證。

保證數據不重複(exactly-once)

要保證數據不重複,即Exactly once語義。

  • 冪等操作:重複執行不會產生問題,不需要做額外的工作即可保證數據不重複。
  • 業務代碼添加事務操作
    就是說針對每個partition的數據,產生一個uniqueId,只有這個partition的所有數據被完全消費,則算成功,否則算失效,要回滾。下次重複執行這個uniqueId時,如果已經被執行成功,則skip掉。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章