一、概述
- Kafka是一個具有高吞吐量,高拓展性,高性能和高可靠的基于發布訂閱模式的消息隊列,是由領英基于Java和Scala語言開發。通常適合于大數據量的消息傳遞場景,如日志分類,流式數據處理等。
- Kafka的體系結構的核心組件包括:消息生產者,消息消費者,基于消息主題進行消息分類,使用Broker集群進行數據存儲。同時使用Zookeeper進行集群管理,包括主題的分區信息,分區存放的broker信息,每個分區由哪些消費者消費以及消費到哪里的信息。
- Kafka相對于其他MQ,最大的特點是高拓展性,包括消息通過主題拓展,主題通過分區拓展,消息消費通過消費者組拓展,數據存儲通過brokers機器集群拓展。
二、主題與分區
主題Topic
-
Kafka作為一個消息隊列,故需要對消息進行分類,Kafka是通過Topic主題來對消息進行分類的,即Kafka可以根據需要定義多個主題,每個消息屬于一個主題。如下為kafka提供的用于創建,刪除和查看kafka當前存在的主題的命令行工具:
-
創建主題:指定分區數partitions,分區副本數replication-factor,zookeeper。
./kafka-topics.sh --create --topic mytopic --partitions 2 --zookeeper localhost:2181 --replication-factor 2
-
查看某個主題的信息:PartitionCount分區數量,ReplicationFactor分區副本數量;Leader分區leader(負責該分區的讀寫),Isr同步副本。由于在本機存在3個brokers,對應的server.properties的broker.id分別為:0, 1, 2,所以通過--describe選項查看的mytopic的詳細信息可知:
mytopic的分區0是分區leader為broker2,同步副本為broker1和broker2;分區1的分區leader為broker0,同步副本為broker0和broker2。xyzdeMacBook-Pro:bin xyz ./kafka-topics.sh --describe --topic mytopic --zookeeper localhost:2181 Topic:mytopic PartitionCount:2 ReplicationFactor:2 Configs: Topic: mytopic Partition: 0 Leader: 2 Replicas: 2,1 Isr: 2,1 Topic: mytopic Partition: 1 Leader: 0 Replicas: 0,2 Isr: 0,2
-
查看所有主題信息:
./kafka-topics.sh --list --zookeeper localhost:2181
-
刪除主題:
xyzdeMacBook-Pro:bin xieyizun$ ./kafka-topics.sh --delete --topic mytopic --zookeeper localhost:2181 Topic mytopic is marked for deletion. Note: This will have no impact if delete.topic.enable is not set to true.
-
主題就相當于一個個消息管道,同一個主題的消息都在同一個管道流動,不同管道的消息互不影響。
作為一個高吞吐量和大數據量的消息隊列,如果一個主題的消息非常多,由于所有消息都需要排隊處理,故很容易導致性能問題。所以在主題的基礎上可以對主題進行進一步地分類,這個就是分區。
分區Partition
- 分區是對主題Topic的拓展,每個主題可以包含多個分區,每個分區包含整個主題的全部信息的其中一部分,全部信息由所有分區的消息組成。所以主題就相當于一根網線,而分區是網線里面五顏六色的數據傳輸線。
-
有序性:由于主題的消息分散到了各個分區中,故如果存在多個分區,則該主題的消息整體上是無序的,而每個分區相當于以隊列,內部的消息是局部有序的。所以如果需要保證主題整體的消息有序,則只能使用一個分區。如圖所示:mytopic包含3個partition分區,每個分區內部是一個消息隊列。
在這里插入圖片描述
分區副本Replication:高可靠性
- 為了實現可靠性,即避免分區的消息丟失,每個分區可以包含多個分區副本,通過數據冗余存儲來實現數據的高可靠性。
- 每個分區的多個副本中,只有一個作為分區Leader,由該分區Leader來負責該分區的所有消息讀寫操作,其他分區副本作為Followers從該分區Leader同步數據。
分區副本的存儲
- 為了避免某個broker機器節點故障導致數據丟失,每個分區的多個副本需要位于不同的broker機器節點存放,這樣當某個broker機器節點出現故障不可用時,可以從將其他broker機器節點選舉該分區的另一個副本作為分區Leader繼續進行該分區的讀寫。
- 所以brokers機器集群節點的數量需要大于或者等于最大的分區副本數量,否則會導致主題創建失敗。
同步副本Isr
分區Leader的選舉是由zookeeper負責的,因為zookeeper存儲了每個分區的分區副本和分區Leader信息。如果當前分區Leader所在的broker機器節點掛了,則zookeeper會從其他分區副本選舉產生一個新的分區Leader。
zookeeper不是隨便選舉一個分區副本作為新的分區Leader的,而是從該分區的同步副本Isr集合中選舉。所謂同步副本就是該副本的數據是與分區Leader保持同步。“幾乎”一致的,故在分區leader掛了時,可以減少數據的丟失。
-
一個分區副本成為同步副本的條件如下:如果當前不存在同步副本,分區leader可以拋異常拒絕數據寫入。
replica.lag.time.max.ms,默認10000,副本未同步數據的時間 replica.lag.max.messages,4000,副本滯后的最大消息條數
三、生產者
消息路由
-
前面介紹了kafka通過主題和分區來對消息進行分類存儲,而消息生產者負責生產消息,指定每個消息屬于哪個主題的哪個分區。即kafka的消息路由是由生產者負責的,生產者在生成消息時需要指定消息的主題和分區。如圖:producer將生成mytopic主題的三個消息分別傳給分區0,1,2的分區leader對應的broker100,broker101,broker102。
在這里插入圖片描述 - 分區的指定的可選:
- 如果生產者不顯示指定消息的分區,則kafka的Producer API默認是基于round-robin輪詢來發送消息給該主題的多個分區的。
- 生產者可以指定一個key,然后kafka會基于該key的hash值來將相同key的消息路由到同一個分區,從而實現相同key的消息的有序,如在股票行情中,使用股票代號作為
key,從而實現同一股票的分時價格數據有序。 - 自定義partition:可以實現Partitioner接口并重寫partition方法來自定義分區路由。
消息ack實現可靠性
- 生產者負責生成并發送消息給各個主體的各個分區,由于網絡的不穩定性和分區leader所在的broker機器可能出現故障,故需要一種機制來保證消息的可靠性傳輸,這種機制就是ack機制,即生產者發送一個消息之后,只有在收到kafka服務器返回的ack確認之后才認為該消息成功發送,否則進行消息重發。其中kafka的消息重發是冪等的。
- ack實現消息可靠性和整體的吞吐量和性能需要取一個折中,故kafka的ack機制相關配置參數如下:主要包括acks,retries,producer.type。
-
acks參數:消息確認
acks=0:發送后則生產者立即返回,不管消息上是否寫入成功,這種吞吐量和性能最好,但是可靠性最差; acks=1:默認,發送后,等待分區leader寫入成功返回ack則生產者返回; acks=-1: 發送后,不僅需要分區leader的ack,還需要等待所有副本寫入后的ack,可靠性最高,吞吐量和性能最差。
retries:可重試錯誤的重試次數,如返回LEADER_NOT_AVALIAVLE錯誤時,則需要重試;
producer.type:發送類型,sync同步發送(默認)和async異步發送(batch發送)
-
消費生成與消費測試
-
kafka提供了命令行工具來生產消息,方便進行主題的測試:執行如下命令后,則可以在命令行輸入各種消息,然后可以在另一個終端消費查看消息是否生成成功。
./kafka-console-producer.sh --topic mytopic --broker-list localhost:19092
-
消息消費命令:
./kafka-console-consumer.sh --topic quote-depth --zookeeper localhost:2181
四、Broker集群
- broker機器集群就是kafka的服務端實現,主要負責存儲主題各分區的消息,負責接收生產者的數據寫入請求,處理消費者的數據讀取請求。
分區數據存儲
- 每個broker可以存放多個主題的多個分區的消息,而這些統計信息,即某個分區位于哪個broker是由zookeeper維護的。
- 同一個分區的多個分區副本需要位于不同的broker中,從而避免某個broker機器故障導致該分區的數據丟失。
-
具體存儲關系如圖:紅色的為分區leader,綠色的為分區副本,同一個分區的不同分區副本位于不同的broker中。
在這里插入圖片描述
請求處理模型
-
kafka基于C/S架構,broker作為kafka的服務端實現,生產者和消費者作為kafka的客戶端實現,故broker需要接收生產者和消費者的連接請求并處理,如下為該請求處理模型示意圖:
在這里插入圖片描述
五、消費者
- 消費者負責消費某個主題的某個分區的數據,為了實現可拓展性,實現同一個分區的數據被多種不同的業務重復利用,kafka定義了消費者組的概念,即每個消費者屬于一個消費者組。
消息重復消費
同一個消費者組:單播
- 一個主題的其中一個分區只能被同一個消費者組中的一個消費者消費,消費者內的一個消費者可以消費多個主題的多個分區,從而避免消費者組內對同一個分區的消息的重復消費。
- 如股票的股價提醒當中,對于同一只股票只能由同一個消費者組組的一個消費者線程處理,否則會導致重復提醒。
不同消費者組:廣播
- 不同的消費者組代表不同的業務,每個消費者組都可以有一個消費者對同一個主題的同一個分區消費一次,所以實現了消息的重復消費利用,實現了消費的高度可拓展性。
-
具體消費者組與消費者的消費情況如圖所示:broker1的分區0分別被消費者組A的消費者C1和消費者組B的消費者C3消費。
在這里插入圖片描述
消息消費跟蹤
每個消費者在消費某個主題的一個分區的消息時,基于offset機制來保證對該分區所有消息的完整消費和通過修改offset來實現對該分區消息的回溯。
-
消費者每消費該分區內的一個消息,則遞增消費offset并上傳給zookeeper來維護,使用zookeeper維護的好處時,假如該消費者掛了,則zookeeper可以從該消費者組選擇另外一個消費者從offset往后繼續消費,避免數據的重復消費或者漏掉消費。其中上傳提交offset給zookeeper可以是自動提交或者由應用程序控制手動提交。具體通過參數來定義:
enable.auto.commit:默認為true,即自動提交,是Kafka Consumer會在后臺周期性的去commit。 auto.commit.interval.ms:自動提交間隔。范圍:[0,Integer.MAX],默認值是 5000 (5 s)
自動提交offset
- 消費者消費消息默認是自動提交offset給zookeeper的。使用自動提交的好處是編程簡單,應用代碼不需要處理offset的提交。缺點是可能導致消息的丟失,即當消費者從broker讀取到了消息,然后自動提交offset給zookeeper,如果消費者在處理該消息之前掛了,則會導致消息沒有處理而丟失了,因為此時已經上傳了offset給zookeeper,則下一個消費者不會消費該消息了。
- 故自動提交模型是at most once,即最多消費一次,可能存在消息丟失。
手動提交offset
- 如果關掉自動提交,即設置enable.auto.commit為false,則需要應用程序消費消息后手動提交。這種方式的好處是消費者線程可以在成功處理該消息后才提交到zookeeper,如果處理中途掛了,則不會上傳給zookeeper,下個消費者線程可以繼續處理該消息。所以缺點是可能導致消費的重復消費,如消費者線程在成功處理該消息后,如寫入數據庫成功了,但是在提交之前消費者線程掛了沒有提交該offset給zookeeper,則會造成重復消費。
- 故手動提交模型是at least once,即至少消費一次,可能存在重復消費。