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