Kafka Streams适用于构建应用程序和微服务的客户端库,其中输入和输出数据存储在kafka集群中。它结合了在客户端编写和部署标准Java和Scala应用程序的简便性,以及kafka服务器端集群技术的优势。
概述:
1)kafka Streams类库用于构建实时计算处理应用
2)输入和输出来源于kafka
3)编程语言支持Java和Scala
4)集结kafka服务器端集群技术优势(可靠,容错等特点)
核心概念:
·Topology(拓扑任务):代表是一个流处理计算的任务,在一个拓扑任务中可能包含了多个数据处理逻辑。
·Stream(数据流):代表是一个无限的,不断更新的Record的数据集。
·States(状态):代表的流处理计算的中间结果,状态数据可以用于结果累积和状态恢复容错。
·Processor(处理器):代表的是拓扑任务中的一个处理逻辑:Topology由1到N和Processor构成。
·Time(时间)
事件时间:数据的产生时间
摄入时间:流数据应用将流数据拉取到应用的时间。
处理时间:流数据应用处理数据的时间。
结论:事件时间<=摄入时间<=处理时间
开发简单应用:
导入依赖:
<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-streams -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
<version>2.3.0</version>
</dependency>
public class WordCountApplication {
public static void main(String[] args) {
// 1. 准备配置对象
Properties prop = new Properties();
//对应的端口,记得在windows做主机映射
prop.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "HadoopNode:9092");
prop.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass()); // 构建Key序列化和反序列化器
prop.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass()); // 构建Value序列化和反序列化器
prop.put(StreamsConfig.APPLICATION_ID_CONFIG, "wordcount"); // 应用标识 以后会作为消费组的ID
// 2. 构建流式数据源
// topic input:英文短语 Hello World | Hello Kafka
// topic output: Hello 2 | World 1 | Kafka 1
StreamsBuilder streamsBuilder = new StreamsBuilder();
KStream<String, String> stream = streamsBuilder.stream("input");
// 3. 应用处理逻辑
// Hello World --> Hello | World --> 分区 --> 计数
stream
// 展开
.flatMap(new KeyValueMapper<String, String, Iterable<KeyValue<String, String>>>() {
public Iterable<KeyValue<String, String>> apply(String key, String value) {
ArrayList<KeyValue<String, String>> keyValues = new ArrayList<KeyValue<String, String>>();
if (value != null) {
String[] words = value.split("\\s");
for (String word : words) {
keyValues.add(new KeyValue<String, String>(key, word)); // key line => key word
}
}
return keyValues;
}
})
// 映射(word,1) k=null v=word
.map(new KeyValueMapper<String, String, KeyValue<String, Long>>() {
public KeyValue<String, Long> apply(String key, String value) {
return new KeyValue<String, Long>(value, 1L); // Hello 1L
}
})
// 分组 根据单词进行分组
.groupByKey(Grouped.with(Serdes.String(), Serdes.Long()))
// 聚合操作
// 统计key相同value个数 返回结果表对象
.count(Materialized.<String, Long, KeyValueStore<Bytes, byte[]>>as("state"))
.toStream()
.print(Printed.<String, Long>toSysOut());
// 4. 构建拓扑任务
KafkaStreams kafkaStreams = new KafkaStreams(streamsBuilder.build(), prop);
// 5. 启动流式计算
kafkaStreams.start();
}
}
原理剖析:
Kafka Streams任务的并行度
Kafka Steam基于应用程序的输入流分区创建固定数量的Task,每个任务(Task)分配来自输入流的分区列表(即Kafka主题)。分区到任务的分配永远不会改变,因此每个任务都是应用程序的固定平行单元。然后,任务可以根据分配的分区实例化自己的处理器拓扑,他们还为每个封面配的分区维护一个缓冲区,并从这些记录缓冲区一次一个的处理消息。因此,流任务可以独立并行地处理,无需人工干预。
用户可以启动多个KafkaStream实例,这样等价启动了多个Stream Tread,每个Thread处理1~n个Task。一个Task对应一个分区,因此Kafka Stream流处理的并行度不会超越Topic的分区数。需要值得注意的是Kafka的每个Task都维护着自身的一些状态,线程之间不存在状态共享和通信。因此Kafka在实现流处理的过程中扩展是非常高效的。