Kafka 來源
Kafka
的前身是由LinkedIn
開源的一款產品,2011年初開始開源,加入了 Apache 基金會,2012年從 Apache Incubator 畢業變成了 Apache 頂級開源項目。同時LinkedIn還有許多著名的開源產品。如:
- 分布式數據同步系統
Databus
- 高性能計算引擎
Cubert
- Java異步處理框架
ParSeq
-
Kafka
流處理平臺
Kafka 介紹
Kafka 用于構建實時數據管道和流應用程序。它具有水平可擴展性,容錯性,快速性,并在數千家公司的生產環境中運行。
從官方我們可以知道ApacheKafka
是一個分布式流媒體平臺。這到底是什么意思呢?
流媒體平臺有三個關鍵功能:
- 發布和訂閱記錄數據流,類似于消息隊列或企業消息傳遞系統。
- 有容錯能力的可以持久化的存儲數據流。
- 記錄發生時可以進行流處理。
Kafka 通常用于兩大類應用:
- 構建可在系統或應用程序之間可靠獲取數據的實時流數據管道
- 構建轉換或響應數據流的實時流處理
Kafka 基本概念
- Producer - 消息和數據的生產者,向 Kafka 的一個 Topic 發布消息的進程/代碼/服務。
- **Consumer **- 消息和數據的消費者,訂閱數據(Topic)并且處理其發布的消息的進程/代碼/服務。
- Consumer Group - 邏輯概念,對于同一個 Topic,會廣播不同的 Group,一個Group中,只有一個consumer 可以消費該消息。
- Broker - 物理概念,Kafka 集群中的每個 Kafka 節點。
- Topic - 邏輯概念,Kafka消息的類別,對數據進行區分,隔離。
- Partition - 物理概念,分片,Kafka 下數據存儲的基本單元,一個 Topic 數據,會被分散存儲到多個Partition,每一個Partition是有序的。
- **Replication **- 副本,同一個 Partition 可能會有多個 Replica ,多個 Replica 之間數據是一樣的。
- Replication Leader - 一個 Partition 的多個 Replica 上,需要一個 Leade r負責該 Partition 上與 Produce 和 Consumer 交互
- ReplicaManager - 負責管理當前的 broker 所有分區和副本的信息,處理 KafkaController 發起的一些請求,副本狀態的切換,添加/讀取消息等。
概念的延伸
Partition
- 每一個Topic被切分為多個Partitions
- 消費者數據要小于等于Partition的數量
- Broker Group中的每一個Broker保存Topic的一個或多個Partitions
- Consumer Group中的僅有一個Consumer讀取Topic的一個或多個Partions,并且是唯一的Consumer。
Replication
- 當集群中有Broker掛掉的時候,系統可以主動的使用Replicas提供服務。
- 系統默認設置每一個Topic的Replication的系數為1,可以在創建Topic的時候單獨設置。
Replication特點
- Replication的基本單位是Topic的Partition。
- 所有的讀和寫都從Leader進,Followers只是作為備份。
- Follower必須能夠及時的復制Leader的數據
- 增加容錯性與可擴展性。
Kafka 消息結構
在 Kafka2.0 中的消息結構如下(整理自官網)。
baseOffset: int64 - 用于記錄Kafka這個消息所處的偏移位置
batchLength: int32 - 用于記錄整個消息的長度
partitionLeaderEpoch: int32
magic: int8 (current magic value is 2) - 一個固定值,用于快速判斷是否是Kafka消息
crc: int32 - 用于校驗信息的完整性
attributes: int16 - 當前消息的一些屬性bit 0~2:
0: no compression
1: gzip
2: snappy
3: lz4bit 3: timestampType
? bit 4: isTransactional (0 means not transactional)
? bit 5: isControlBatch (0 means not a control batch)
? bit 6~15: unusedlastOffsetDelta: int32
firstTimestamp : int64
maxTimestamp: int64
producerId: int64
producerEpoch: int16
baseSequence: int32
records:length: varint
attributes: int8bit 0~7: unused
timestampDelta: varint
offsetDelta: varint
keyLength: varint
key: byte[]
valueLen: varint
value: byte[]
Headers => [Header]headerKeyLength: varint
headerKey: String
headerValueLength: varint
Value: byte[]
關于消息結構的一些釋義。
- Offset -用于記錄Kafka這個消息所處的偏移位置
- Length - 用于記錄整個消息的長度
- CRC32 - 用于校驗信息的完整性
- Magic - 一個固定值,用于快速判斷是否是Kafka消息
- Attributes - 當前消息的一些屬性
- Timestamp - 消息的時間戳
- Key Length - key的長度
- Key - Key的具體值
- Value Length - 值的長度
- Value - 具體的消息值
Kafka 優點
- 分布式 - Kafka是分布式的,多分區,多副本的和多訂閱者的,基于Zookeeper調度。
- 持久性和擴展性 - Kafka使用分布式提交日志,這意味著消息會盡可能快地保留在磁盤上,因此它是持久的。同時具有一定的容錯性,Kafka支持在線的水平擴展,消息的自平衡。
- 高性能 - Kafka對于發布和訂閱消息都具有高吞吐量。 即使存儲了許多TB的消息,它也保持穩定的性能。且延遲低,適用高并發。時間復雜的為o(1)。
Kafka 應用
- 用于聚合分布式應用程序中的消息。進行操作監控。
- 用于跨組織的從多個服務收集日志,然后提供給多個服務器,解決日志聚合問題。
- 用于流處理,如Storm和Spark Streaming,從kafka中讀取數據,然后處理在寫入kafka供應用使用。
Kafka 安裝
安裝 Jdk
具體步驟此處不說。
安裝 Kafka
直接官方網站下載對應系統的版本解壓即可。
由于Kafka對于windows和Unix平臺的控制腳本是不同的,因此如果是windows平臺,要使用bin\windows\
而不是bin/
,并將腳本擴展名更改為.bat
。以下命令是基于Unix平臺的使用。
# 解壓
tar -xzf kafka_2.11-2.0.0.tgz
# 啟動Zookeeper
bin/zookeeper-server-start.sh config/zookeeper.properties
# 啟動Kafka
bin/kafka-server-start.sh config/server.properties
# 或者后臺啟動
bin/kafka-server-start.sh config/server.properties &
讓我們創建一個名為“test”的主題,它只包含一個分區,只有一個副本:
`> bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
如果我們運行list topic命令,我們現在可以看到該主題:
`> bin/kafka-topics.sh --list --zookeeper localhost:2181 test
或者,您也可以將代理配置為在發布不存在的主題時自動創建主題,而不是手動創建主題。
查看Topic的信息
./kafka-topics.sh --describe --zookeeper localhost:2181 --topic Hello-Kafka
運行生產者,然后在控制臺中鍵入一些消息以發送到服務器。
> bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
This is a message
This is another message`
運行消費者,查看收到的消息
> bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning
> This is a message
> This is another message
Kafka 工程實例
POM 依賴
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.1.0</version>
</dependency>
生產者
編寫生產者 Java 代碼。關于 Properties 中的值的意思描述可以在官方文檔中找到 http://kafka.apache.org/ 。下面的生產者向 Kafka 推送了10條消息。
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.Properties;
/**
* <p>
* Kafka生產者,發送10個數據
*
* @Author niujinpeng
* @Date 2018/11/16 15:45
*/
public class MyProducer {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "192.168.110.132:9092");
props.put("acks", "all");
props.put("retries", 0);
props.put("batch.size", 16384);
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer<String, String> producer = new KafkaProducer<>(props);
for (int i = 0; i < 10; i++) {
producer.send(new ProducerRecord<String, String>("test", Integer.toString(i), Integer.toString(i)));
}
producer.close();
}
}
消費者
編寫消費者 Java 代碼。
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import java.util.Arrays;
import java.util.Properties;
/**
* <p>
* Kafka消費者
*
* @Author niujinpeng
* @Date 2018/11/19 15:01
*/
public class MyConsumer {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "192.168.110.132:9092");
props.put("group.id", "test");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("test"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records) {
System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
}
}
}
}
可以在控制臺看到成功運行后的輸出,由 offset 可以看到已經消費了10條消息。
INFO | Kafka version : 2.0.0
INFO | Kafka commitId : 3402a8361b734732
INFO | Cluster ID: 0Xrk5M1CSJet0m1ut3zbiw
INFO | [Consumer clientId=consumer-1, groupId=test] Discovered group coordinator 192.168.110.132:9092 (id: 2147483647 rack: null)
INFO | [Consumer clientId=consumer-1, groupId=test] Revoking previously assigned partitions []
INFO | [Consumer clientId=consumer-1, groupId=test] (Re-)joining group
INFO | [Consumer clientId=consumer-1, groupId=test] Successfully joined group with generation 4
INFO | [Consumer clientId=consumer-1, groupId=test] Setting newly assigned partitions [test-0]
offset = 38, key = 0, value = 0
offset = 39, key = 1, value = 1
offset = 40, key = 2, value = 2
offset = 41, key = 3, value = 3
offset = 42, key = 4, value = 4
offset = 43, key = 5, value = 5
offset = 44, key = 6, value = 6
offset = 45, key = 7, value = 7
offset = 46, key = 8, value = 8
offset = 47, key = 9, value = 9
問題
如果java.net.InetAddress.getCanonicalHostName
取到的是主機名。需要修改 Kafka 的配置文件。
vim server.properties
# x.x.x.x是服務器IP
advertised.listeners=PLAINTEXT://x.x.x.x:9092
<完>
本文原發于個人博客:https://www.codingme.net 轉載請注明出處