1.DSL概述
Kafka Streams DSL(Domain Specific Language)构建于Streams Processor API之上。它是大多数用户推荐的,特别是初学者。大多数数据处理操作只能用几行DSL代码表示。在 Kafka Streams DSL 中有这么几个概念
KTable
、KStream
和GlobalKTable
KStream
是一个数据流,可以认为所有记录都通过Insert only
的方式插入进这个数据流里。而KTable代表一个完整的数据集,可以理解为数据库中的表。由于每条记录都是Key-Value对,这里可以将Key理解为数据库中的Primary Key,而Value可以理解为一行记录。可以认为KTable中的数据都是通过Update only
的方式进入的。也就意味着,如果KTable对应的Topic中新进入的数据的Key已经存在,那么从KTable只会取出同一Key对应的最后一条数据
,相当于新的数据更新了旧的数据。
以下图为例,假设有一个KStream和KTable,基于
同一个Topic
创建,并且该Topic中包含如下图所示5条数据。此时遍历KStream将得到与Topic内数据完全一样的所有5条数据,且顺序不变。而此时遍历KTable
时,因为这5条记录中有3个不同的Key,所以将得到3条记录,每个Key对应最新的值,并且这三条数据之间的顺序与原来在Topic中的顺序保持一致。这一点与Kafka的日志compact
相同。
此时如果对该KStream和KTable分别基于key做Group,对Value进行Sum,得到的结果将会不同。对KStream的计算结果是<Jack,4>,<Lily,7>,<Mike,4>。而对Ktable的计算结果是<Mike,4>,<Jack,3>,<Lily,5>。
GlobalKTable: 和KTable类似,不同点在于KTable只能表示一个分区的信息,但是GlobalKTable表示的是全局的状态信息。
2.DSL 实战
基于High-level的单词计数的实例
2.1 编写应用
package com.hw.highlevel;
import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.streams.*;
import org.apache.kafka.streams.kstream.KStream;
import org.apache.kafka.streams.kstream.KTable;
import org.apache.kafka.streams.kstream.Produced;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* @aurhor:fql
* @date 2019/9/2 12:52
* @type:
*/
public class WordCountApplicaton {
public static void main(String[] args) {
//1.创建kafka Streaming的配置对象
Properties properties = new Properties();
properties.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG,"node1:9092,node2:9092,node3:9092");
//指定key的序列化器和反序列化器
properties.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
properties.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG,Serdes.String().getClass());
properties.put(StreamsConfig.APPLICATION_ID_CONFIG,"wordDSl");
//指定线程数量
properties.put(StreamsConfig.NUM_STREAM_THREADS_CONFIG,2);
//2.DSL编程
StreamsBuilder streamsBuilder = new StreamsBuilder();
KStream<String, String> kStream = streamsBuilder.stream("t9");
//进行展开操作
KTable<String, Long> kTable = kStream.flatMap((String k, String v)->{
String[] words = v.split(" ");
List<KeyValue<String, String>> list = new ArrayList<>();
for (String word : words) {
KeyValue<String, String> keyValue = new KeyValue<>(k, word);
list.add(keyValue);
}
return list;
})
// 将v相同的键值对归为一类
.groupBy((k, v) -> v)
// 统计k相同的v的数量
.count();
//将计算的结果保存到t10的topic中
kTable.toStream().to("t10", Produced.with(Serdes.String(),Serdes.Long()));
//3.创建kafka Streaming应用
//打印Topology关系
Topology topology = streamsBuilder.build();
System.out.println(topology.describe());
KafkaStreams kafkaStreams = new KafkaStreams(topology, properties);
//4.启动
kafkaStreams.start();
}
}
2.2 进行测试
输入测试数据
输出结果展示
[root@node3 kafka_2.11-2.2.0]# bin/kafka-console-consumer.sh --bootstrap-server node1:9092,node2:9092,node3:9092 \
> --topic t10 \
> --from-beginning \
> --formatter kafka.tools.DefaultMessageFormatter \
> --property print.key=true \
> --property print.value=true \
> --property key.deserializer=org.apache.kafka.common.serialization.StringDeserializer \
> --property value.deserializer=org.apache.kafka.common.serialization.LongDeserializer
hadoop 1
hello 2
kafka 1
hello 5