使用kafka Streams统计单词出现的次数
1.实现逻辑:
统计生产者生产的消息,处理逻辑:统计每个单词出现的次数,并将结果输出到目标主题中
2.Producer端:
生产指定格式的数据:
生产数据: kafka-console-producer.sh --topic t1 --broker-list zjuserMaster:9092
dihf java pyton java scala scala java c c++ java
java java js c c java
^[[A
java js kjs =^Hj
java js java scala scala
java js java
java js java js
3.处理逻辑代码
`package com.zj.geek.kafka.StreamsDSL;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.streams.KafkaStreams;
import org.apache.kafka.streams.StreamsBuilder;
import org.apache.kafka.streams.StreamsConfig;
import org.apache.kafka.streams.kstream.KStream;
import org.apache.kafka.streams.kstream.KTable;
import org.apache.kafka.streams.kstream.Produced;
import java.util.Arrays;
import java.util.Locale;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
/**
* @author Zhou Jian
* @Description kafka DSL处理实例
* @createTime 2020年12月19日 15:07:00
*/
/*
统计生产者生产的消息,处理逻辑:统计每个单词出现的次数,并将结果输出到目标主题中
*/
public class DSL {
public static void main(String[] args) {
Properties props = new Properties() {{
// application.id 是 Streams 程序中非常关键的参数,你必须要指定一个集群范围内唯一的字符串来标识你的 Kafka Streams 程序
put(StreamsConfig.APPLICATION_ID_CONFIG, "wordcount-stream-demo");
// 连接的kafka目标集群
put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "zjuserMaster:9092");
// 序列化和反序列化类
put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
// 自动重置消费者位移 earliest: automatically reset the offset to the earliest offset
put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
}};
// 创建StreamsBuilder对象
StreamsBuilder builder = new StreamsBuilder();
// 从主题t1中读取消息
KStream<String, String> source = builder.stream("t1");
// 将流转换为表 --> KStream ~ KTable
// String, Long --> 单词,出现的次数
KTable<String, Long> counts = source
/*
先对单词进行分割,这里我用到了 flatMapValues 方法,
代码中的 Lambda 表达式实现了从消息中提取单词的逻辑。
由于 String.split() 方法会返回多个单词,
因此我们使用 flatMapValues 而不是 mapValues。
原因是,前者能够将多个元素“打散”成一组单词,
而如果使用后者,我们得到的就不是一组单词,而是多组单词了。
*/
.flatMapValues(value -> Arrays.asList(value.toLowerCase(Locale.getDefault()).split(" ")))
/*
调用 groupBy 方法对单词进行分组。
由于是计数,相同的单词必须被分到一起,
*/
.groupBy((key, value) -> value)
/*
然后就是调用 count 方法对每个出现的单词进行统计计数,
并保存在名为 counts 的 KTable 对象中。
*/
.count();
/*
最后,我们将统计结果写回到 Kafka 中。由于 KTable 是表,是静态的数据,
因此这里要先将其转换成 KStream,
然后再调用 to 方法写入到名为 wordCount 的主题中。
此时,counts 中事件的 Key 是单词,而 Value 是统计个数,
因此我们在调用 to 方法时,同时指定了 Key 和 Value 的序列化器,分别是字符串序列化器和长整型序列化器。
*/
counts.toStream().to("wordCount", Produced.with(Serdes.String(), Serdes.Long()));
// 构造 KafkaStreams 实例并启动它了
KafkaStreams streams = new KafkaStreams(builder.build(), props);
CountDownLatch latch = new CountDownLatch(1);
Runtime.getRuntime().addShutdownHook(new Thread("wordcount-stream-demo-jvm-hook") {
@Override
public void run() {
streams.close();
latch.countDown();
}
});
try {
streams.start();
latch.await();
} catch (final Throwable e) {
System.exit(1);
}
System.exit(0);
}
}`
4.结果显示
kafka-console-consumer.sh --bootstrap-server zjuserMaster:9092 \
--topic wordCount
--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
dihf 1
java 4
java 7
1
java 8
kjs 1
j 1
java 10
java 12
java 14
pyton 1
scala 2
浙公网安备 33010602011771号