https://blog.csdn.net/weixin_38910645/article/details/107252855
什么是Kafka Stream
Kafka Streams是一个用于处理和分析数据的客户端库。它先把存储在Kafka中的数据进行处理和分析,然后将最终所得的数据结果回写到Kafka或发送到外部系统去。它建立在一些非常重要的流式处理概念之上,例如:适当区分事件时间和处理时间、窗口支持,以及应用程序状态的简单(高效)管理。同时,它也基于Kafka中的许多概念,例如通过划分主题进行扩展。由于这个原因,它作为一个轻量级的库可以集成到应用程序中去。这个应用程序可以根据需要独立运行、在应用程序服务器中运行、作为Docker容器,或通过资源管理器(如Mesos)进行操作。通过利用Kafka的并行模型,Kafka Streams可以透明地处理同一应用程序的多个实例的负载平衡。
Kafka Streams的一些特点:
- 设计为简单轻量级的客户端库,可以轻松地将其嵌入任何Java应用程序中,并与用户为其流式应用程序所拥有的任何现有打包,部署和操作工具集成
- 除了Apache Kafka本身作为内部消息传递层之外,对系统没有任何外部依赖性
- 使用Kafka的分区模型来水平扩展处理
- 支持容错本地状态,该状态可以进行非常快速有效的状态操作,例如:窗口连接和聚合
- 支持一次精确的处理语义,以确保在处理过程中Streams客户端或Broker发生故障时,每条记录也只处理一次
- 采用一次一个记录的处理以实现毫秒级的处理延迟
- 支持基于事件时间的窗口操作以及记录的无序到达
- 提供必要的流处理原语,以及高级Streams DSL和低级Processor API
处理示例
public class KafkaWordCount {
public static void main(String[] args) {
Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "wordcount-application");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.56.105:9092");
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
StreamsBuilder builder = new StreamsBuilder();
// 从输入主题TextLinesTopic构造一个KStream
// 其中消息值代表文字行
KStream<String, String> textLines = builder.stream("TextLinesTopic");
KTable<String, Long> wordCounts = textLines
// 用空格将每行文本分割成多个单词
.flatMapValues(textLine -> Arrays.asList(textLine.toLowerCase().split("\\W+")))
// 将文本单词分组为消息键
.groupBy((key, word) -> word)
// 计算每个单词(消息键)出现次数
.count(Materialized.<String, Long, KeyValueStore<Bytes, byte[]>>as("counts-store"));
// 将运行结果作为流存储到WordsWithCountsTopic主题
wordCounts.toStream().to("WordCountTopic", Produced.with(Serdes.String(), Serdes.Long()));
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();
}
}
示例程序将从TextLinesTopic主题中读取内容,对每个读取的消息执行WordCount算法的计算,并将其当前结果连续写入输出主题流WordCountTopic中。因此,除了日志条目外,将没有任何STDOUT输出,因为结果将回写到Kafka中。
filter
public class StreamFilter {
public static void main(String[] args) {
Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "filter-application0");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.56.105:9092");
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> kstream = builder.stream("topic-02");
kstream.filter(new Predicate<String, String>() {
@Override
public boolean test(String key, String value) {
return key.startsWith("keyB");
}
}).foreach(new ForeachAction<String, String>() {
@Override
public void apply(String key, String value) {
System.out.println(key + " => " + value);
}
});
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();
}
}
flatmap
public class StreamFlatmap {
public static void main(String[] args) {
Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "flatmap-application");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.56.105:9092");
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> kstream = builder.stream("topic-02");
KStream<String, Integer> transformed = kstream.flatMap(
(key, value) -> {
List<KeyValue<String, Integer>> result = new LinkedList<>();
result.add(KeyValue.pair(value.toUpperCase(), 1000));
result.add(KeyValue.pair(value.toLowerCase(), 9000));
return result;
}
);
transformed.foreach((key, value) -> System.out.println(key + " => " + value));
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();
}
}
groupByKey
public class StreamGroupby {
public static void main(String[] args) {
Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "groupby-application");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.56.105:9092");
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> kstream = builder.stream("topic-02");
KTable<String, String> table = kstream.toTable();
// 按新Key和其类型对流进行分组
KGroupedStream<String, String> groupedStream = kstream.groupBy(
new KeyValueMapper<String, String, String>() {
@Override
public String apply(String key, String value) {
return value;
}
},
Grouped.with(
// key 可以被修改
Serdes.String(),
Serdes.String())
);
// 按新的Key和其类型对表进行分组,同时修改值和值类型
KGroupedTable<String, String> groupedTable = table.groupBy(
new KeyValueMapper<String, String, KeyValue<String, String>>() {
@Override
public KeyValue<String, String> apply(String key, String value) {
return KeyValue.pair(key, key + "," + value);
}
},
Grouped.with(
// 类型可以被修改
Serdes.String(),
Serdes.String())
);
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();
}
}
有状态转换
public class StreamLeftJoin {
public static void main(String[] args) {
Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "leftjoin-application");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.56.105:9092");
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
StreamsBuilder builder = new StreamsBuilder();
KStream<String, String> left = builder.stream("topic-03");
KStream<String, String> right = builder.stream("topic-04");
KStream<String, String> joined = left.selectKey(((key, value) -> value.split(",")[0]))
.leftJoin(right.selectKey((key, value) -> value.split(",")[0]),
(leftValue, rightValue) -> "left=" + leftValue + ", right=" + rightValue, /* ValueJoiner */
JoinWindows.of(Duration.ofMinutes(5)),
Joined.with(
// key
Serdes.String(),
// left value
Serdes.String(),
// right value
Serdes.String())
);
joined.foreach((key, value) -> System.out.println(key + " => " + value));
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();
}
}