Kafka架構設計簡介 原 薦

1、概述

Apache Kafka 是一個分佈式高吞吐量的流消息系統,Kafka建立在ZooKeeper同步服務之上。它與Apache Storm和Spark完美集成,用於實時流數據分析,與其他消息傳遞系統相比,Kafka具有更好的吞吐量,內置分區,數據副本和高度容錯功能,因此非常適合大型消息處理應用場景。

2、什麼是消息系統

消息系統負責將數據從一個應用程序傳輸到另一個應用程序,完美的實現了兩個應用之間的解耦,消息的傳遞模式分爲兩種:

(1)點對點(Point to Point)模式

在點對點系統中,消息被保存在一個隊列中。多個消費者可以共同消費隊列中的消息,但是一條消息只能被一個消費者消費

(2)發佈訂閱(pub-sub)模式

在發佈 - 訂閱系統中,消息被保存在一個主題(topic)中。消息發佈者將消息發佈到topic中,多個訂閱topic的訂閱者會同時收到該消息。在發佈 - 訂閱系統中,消息生產者稱爲發佈者,消息消費者稱爲訂閱者

3、Kafka特點

  • 可靠性: Kafka是分佈式的、可分區的、數據可備份的、高度容錯的
  • 可擴展性: 在無需停機的情況下實現輕鬆擴展
  • 消息持久性: Kafka支持將消息持久化到本地磁盤
  • 性能:Kafka的消息發佈訂閱具有很高的吞吐量,即便存儲了TB級的消息,它依然能保持穩定的性能

4. Kafka架構組件

(1)Broker

一個獨立的Kafka服務器被稱爲broker,broker 接收來自生產者的消息,爲消息設置偏移量,並保存消息到磁盤中。 broker 爲消費者提供服務,對讀取分區的請求作出響應,返

回已經提交到磁盤上的消息。

(2)Topic

Kafka的消息通過Topic主題來分類,Topic類似於關係型數據庫中的表,每個Topic包含一個或多(Partition)分區

注:一個Topic主題到底該設置多少分區合理呢?一般:分區數目 = Topic的吞吐量 / Consumer的吞吐量

(3)Partition

分區,一個Topic包含一個或多個Partition分區,多個分區會分佈在Kafka集羣的不同服務節點上,消息以追加的方式寫入一個或多個分區中。

注:由於一個主題(Topic)一般包含多個(Partition)分區,因此無法在整個主題範圍內保證消息的順序,但可以保證消息在單個分區內的順序,如將Topic分爲3個區,向該Topic中發送消息分別爲:A、B、C、D、E、F、G、H、I 共9條消息,那麼當消息寫入分區後可能爲如下圖

(4)Producer

消息的生產者,負責發佈消息到Kafka broker,生產者在默認情況下把消息均衡地分佈到主題的所有分區上,用戶也可以自定義分區器來實現消息的分區路由。

(5)Consumer

消息的消費者,從Kafka broker讀取消息的客戶端,消費者把每個分區最後讀取的悄息偏移量保存在 Zookeeper 或 Kafka 上,如果悄費者關閉或重啓,它的讀取狀態不會丟失。

(6)Consumer Group

每個Consumer屬於一個特定的Consumer Group(可爲每個Consumer指定group name,若不指定group name則屬於默認的group),一個或多個Consumer組成的羣組可以共同消費一個Topic中的消息,但每個分區只能被羣組中的一個消費者操作,如下圖,Consumer1對應操作分區0與分區1,Consumer2對應操作分區2

5、Kafka的副本機制

每個分區可以指定n個副本,那麼它可以承受n-1個節點故障,一個分區中的多個副本中有一個副本爲leader,其餘的爲follow

注:zookeeper最多可以承受(n-1)/2個節點故障

副本模型:

(1)同步複製

Producer從zk中找到分區副本的leader,併發送message消息,leader收到消息後立即寫入本地log,然後follow開始pull消息,每個follow將pull到的消息也寫入本地log後,向leader發送消息確認回執,leader在收到所有的follow確認回執後,再向Producer發送確認回執。

(2)異步複製

leader的本地log寫入完成立即向Producer發送確認回執

6、消費者消費消息後的偏移量更新

消費者(Consumer)把每個分區最後讀取的悄息偏移量提交保存在 Zookeeper 或 Kafka 上,如果悄費者關閉或重啓,它的讀取狀態不會丟失,KafkaConsumer API 提供了很多種方式來提交偏移量,但是不同的提交方式會產生不同的數據影響。

(1)自動提交

如果enable.auto.commit被設置爲true,那麼消費者會自動提交當前處理到的偏移量,自動提交的時間間隔爲5s,通過 atuo.commit.interval.ms 屬性設置,自動提交是非常方便,但是自動提交會出現消息被重複消費的風險,可以通過修改提交時間間隔來更頻繁地提交偏移量,減小可能出現重複悄息的時間窗,不過這種情況是無也完全避免的。

(2)手動同步提交

將auto.commit.offset自動提交屬性設置爲false,然後通過調用commitSync()同步提交方法來提交偏移量,該提交方式在發生分區再均衡的時候也會出現重複消息被消費,但相對自動提交來說更加可靠一點。手動同步提交的代碼示例:

while (true) {
	ConsumerRecords<String, String> records = consumer.poll(100);
	for (ConsumerRecord<String, String> record : records) {
		String value = record.value();
		System.out.println("接收到消息:" + value);
	}
	try {
		//同步提交
		consumer.commitSync();
	} catch (Exception e) {
		log.error("commit failed!");
	}
}

(3)手動異步提交

相對手動同步提交的方式不同,該方式提交不會阻塞,異步提交,從而可以提高消息處理的吞吐量,但該提交方式在發生分區再均衡的時候也會出現重複消息被消費

while (true) {
	ConsumerRecords<String, String> records = consumer.poll(100);
	for (ConsumerRecord<String, String> record : records) {
		String value = record.value();
		System.out.println("接收到消息:" + value);
	}
	try {
		//異步提交
        consumer.commitAsync();
	} catch (Exception e) {
		log.error("commit failed!");
	}
}

(4)異步加同步的提交

可以通過異步加同步的組合方式來提交,這樣既保證了消息處理的吞吐量,也最大限度的保證了提交的可靠性。代碼示例:

try {
	while (true) {
		ConsumerRecords<String, String> records = consumer.poll(100);
		for (ConsumerRecord<String, String> record : records) {
			String value = record.value();
			System.out.println("接收到消息:" + value);
		}
		//異步提交
		consumer.commitAsync();
	}
} catch (Exception e) {
	log.error("commit failed!");
} finally {
	try {
		//同步提交
		consumer.commitSync();
	} finally {
		consumer.close();
	}
}

 

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