说明
本文是Confluent Platform 3.0版本中对于Kafka Streams的翻译。
原文地址:https://docs.confluent.io/3.0.0/streams/index.html
看了很多其他人翻译的文档, 还是第一次翻译,有什么翻译的不好的地方还请指出。
Kafka Streams。Apache Kafka开源项目的一个组成部分。是一个功能强大,易于使用的库。用于在Kafka上构建高可分布式、拓展性,容错的应用程序。它建立在流处理的一系列重要功能基础之上,比如正确区分事件事件和处理时间,处理迟到数据以及高效的应用程序状态管理。
下面的列表强调了Kafka Streams的几个关键功能,使得Kafka Streams成为构建流处理应用程序、持续查询、转换和微服务等场景的新选择。
- 功能强大
高拓展性,弹性,容错
有状态和无状态处理
基于事件时间的Window,Join,Aggergations- 轻量级
无需专门的集群
没有外部以来
一个库,而不是框架- 完全集成
100%的Kafka 0.10.0版本兼容
易于集成到现有的应用程序
程序部署无需手工处理(这个指的应该是Kafka多分区机制对Kafka Streams多实例的自动匹配)- 实时性
毫秒级延迟
并非微批处理
窗口允许乱序数据
允许迟到数据
在我们深入Kafka Streams的概念和架构细节以及按部就班认识Kafka Streams之前,我们先来对上面提出的列表做更多的介绍。
- 更简单的流处理:Kafka Streams的设计目标为一个轻量级的库,就像Kafka的Producer和Consumer似得。可以轻松将Kafka Streams整合到自己的应用程序中。对应用程序的额外要求仅仅是打包和部署到应用程序所在集群罢了。
- 除了Apache Kafka之外没有任何其它外部依赖, 并且可以在任何Java应用程序中使用。不需要为流处理需求额外部署一个其它集群。操作和维护团队肯定会很高兴这一点。
- 使用Kafka作为内部消息通讯存储介质,而不是像其它流处理框架似得,重新加入其它外部组件来做消息通讯。Kafka Streams使用Kafka的分区水平拓展来对数据做有序高效的处理。这样同时兼顾了高性能,高扩展性,并使操作简便。这种决策的好处是,你不必了解和调整两个不同的消息传输层(数据在不同伸缩介质中间移动和流处理的独立消息处理层),同样,Kafka的性能和高可靠性方面的改进,都会使得Kafka Streams直接受益。也可以同时借助Kafka社区强大的开发能力。
- 允许和其他资源管理和配置共聚焦集成。因此,Kafka Streams能够更加无缝的集成到现有的开发、打包、部署和业务实践当中去。你可以自由地使用自己喜欢的工具,比如java 应用服务器,Puppet, Ansible,Mesos,Yarn,Docket, 甚至在一台手工运行你自己应用程序进行验证的机器上。
- 支持本地状态容错。这样就可以进行非常高效快速的包含状态的Join和Window 聚合操作。本地状态被保存在Kafka中,在机器故障的时候,其他机器可以自动恢复这些状态继续处理。
- 每次处理一条数据以实现低延时,这对于欺诈监测等场景是至关重要的。这也是Kafka Streams和其他基于微批处理的流处理框架的不同。
此外,Kafka Streams在设计上基于丰富的开发经验,具有很强的实用性。它提供了流处理所有的必要的原语,允许应用程序从Kafka中读取流数据,处理数据并且将结果写回Kafka或者发送到其他外部系统中取。提供了高层次的比如Filter,Map,Join等DSL操作以及低级别API供开发者选择使用。
最后,Kafka Streams为拓展开发者提供帮助,它入门门槛低,开发路径平滑,你可以快速编写和运行一个小规模的应用程序进行验证,因为你完全不需要安装或者了解其他分布式流处理平台。并且只需要将应用程序部署在多个实例上就可以在大批量的生产工作中实现负载均衡。Kafka Streams透明地使用Kafka并行操作模型处理同一应用程序的多个实例来实现负载均衡。
综上所述,Kafka Streams是构建流处理应用中的一个引人注目的选择,请给它一个试用的机会,并运行你的第一个Hello World流处理程序。文档的下一章将带你开始由浅入深编写Kafka Streams应用程序。
Kafka 0.10及更新版本
[可选]额外的avro schema支持: Confluent Schema Registry 3.0.0
#####################################################################################################
说明:
本文是Confluent Platform 3.0版本中对于Kafka Streams的翻译。
原文地址:https://docs.confluent.io/3.0.0/streams/index.html
看了很多其他人翻译的文档,还是第一次翻译,有什么翻译的不好的地方还请指出。
这是Kafka Streams介绍的第二篇,以前的介绍如下:
http://blog.csdn.net/ransom0512/article/details/51971112
本快速入门指南的目标是提供与KafkaStreams的第一个应用程序示例。我们将演示在你的第一个示例程序中,如果使用Kafka Streams库和演示一个简单的端到端的数据流。
值得注意的是,这种快速入门只涵盖了KafkaStreams的表面,这篇文档的剩余部分将会提供更多的细节,我们将在快速入门指南中为你指明方向。
在这个快速入门中,我们将运行包含Apachekafka的一个wordcount演示应用程序。下面代码的关键在于使用Java8的lambda表达式,易于阅读。(摘自WordCountLambdaExample):
//序列化/反序列化Sting和Long类型
final Serde
final Serde
//通过指定输入topic “streams-file-input”来构造KStream实例,
//输入数据就以文本的形式保存在topic “streams-file-input” 中。
//(在本示例中,我们忽略所有消息的key.)
KStream
KStream
//以空格为分隔符,将每行文本数据拆分成多个单词。
//这些文本行就是从输入topic中读到的每行消息的Value。
//我们使用flatMapValues方法来处理每个消息Value,而不是更通用的flatMap
.flatMapValues(value -> Arrays.asList(value.toLowerCase().split("\\W+")))
//我们随后将调用countByKey来计算每个单词出现的次数
//所以我们将每个单词作为map的key。
.map((key, value) -> new KeyValue<>(value, value))
//通过key来统计每个单词的次数
//
//这会将流类型从KStream
//因此我们必须提供String和long的序列化反序列化方法。
//
.countByKey(stringSerde, "Counts")
//转化KTable
.toStream();
//将KStream
wordCounts.to(stringSerde, longSerde, "streams-wordcount-output");
在上面的代码执行过程中,我们将执行如下步骤:
1、 启动一台kafka集群
2、 使用Kafkaconsole producer命令行生产者客户端往Kafka Topic中写入示例输入数据
3、 在Java应用程序中使用kafkaStream库来处理输入数据。这里,我们使用了一个包含kafka的WordCount示例程序。
4、 使用Kafkaconsole consumer命令行消费者客户端检查应用程序的输出。
5、 停止Kafka集群
在本章节中,我们会在一台机器上安装并启动Kafka集群。该集群有一个单节点Kafka(只有一个Broker)外加一个单节点Zookeeper构成。在wordcount演示程序中,这种集群依赖是必须的。我们假定kafka broker运行地址为localhost:9092, Zookeeper本地地址为localhost:2181。
首先,安装oracle JRE或JDK 1.7及以上版本
然后,下载和安装包含Kafka Streams的新版本Apache Kafka. 为此,我们使用Confluent Platform 3.0.0版本。
(下面操作比较简单,所以不翻译了。)
# Download and install Confluent Platform 3.0.0 from ZIP archive
$ wget http://packages.confluent.io/archive/3.0/confluent-3.0.0-2.11.zip
$ unzip confluent-3.0.0-2.11.zip
# *** IMPORTANT STEP ****
# The subsequent paths and commands used throughout this quickstart assume that
# your are in the following working directory:
$ cd confluent-3.0.0/
# Note: If you want to uninstall the Confluent Platform at the end of this quickstart,
# run the following commands.
#
# $ rm -rf confluent-3.0.0/
# $ rm -rf /var/lib/kafka # Data files of Kafka
# $ rm -rf /var/lib/kafka-streams # Data files of Kafka Streams
# $ rm -rf /var/lib/zookeeper # Data files of ZooKeeper
提示:可以通过Installationvia ZIP and TAR archives 和ConfluentPlatform Quickstart 获取更进一步信息。
我们首先启动ZooKeeper实例。该实例将监听本地2181端口。由于这是一个长期运行的服务,你应该在自己的终端中运行。
# Start ZooKeeper. Run this command in its own terminal.
$ ./bin/zookeeper-server-start ./etc/kafka/zookeeper.properties
接下来,我们启动Kakfa的Broker,这将监听本地9092端口,然后连接到我们刚刚启动的Zookeeper实例。这也是一个长期运行的服务,也应该在终端中运行它。
# Start Kafka. Run this command in its own terminal
$ ./bin/kafka-server-start ./etc/kafka/server.properties
现在,我们的单节点kafka集群已经完全运转起来了,我们就可以着手准备输入数据,运行我们的第一个kafka Streams示例程序。
提示:在本章节中,我们将使用内置的命令行工具来输入kakfa数据。在实际使用中,你应该通过其他方式将数据写入Kafka中,比如通过你自己应用程序中的Kafka客户端。
现在,我们将一些输入数据发送到Kafka的topic中,然后由Kafka Streams的应用程序做后续处理。
首先,我们要创建名称为streams-file-input的topic:
$ ./bin/kafka-topics --create \
--zookeeper localhost:2181 \
--replication-factor 1\
--partitions 1\
--topic streams-file-input
下一步,我们生成一些输入数据病保存在本地文件/tmp/file-input.txt中。
$ echo -e "all streams lead to kafka\nhello kafka streams\njoin kafka summit" > /tmp/file-input.txt
生成的文件将包含如下内容:
all streams lead to kafka
hello kafka streams
join kafka summit
最后,我们发送这些数据到input topic
$ cat /tmp/file-input.txt | ./bin/kafka-console-producer --broker-list localhost:9092 --topic streams-file-input
Kafka consoleproducer从stdin中读取数据,并将每一行作为单独的消息发送到kafka的输入流中。该消息的key是null,消息是每行内容,使用字符串编码。
注意: 你可能想知道这样一步步的快速启动和真实流处理系统的差异,在大型的实时的流处理系统中,数据总是在移动的,快速入门的目的仅仅是做功能证明。简单来说,一个端到端的数据管道建立在Kafka和Kafka Streams的各个方面。出于说教的原因,我们故意将快速入门清楚地拆分成一系列分开连续的步骤。
但在实践中,这些步骤通常会看起来有些不同并且会有并发的存在。比如输入数据可能不会来源于本地文件,而是直接从分布式系统中发送的,并且数据将被连续的写入Kafka。类似的,流处理应用程序可能在第一行数据发送之前就已经启动并运行。
现在,我们已经生成了一些输入数据,我们可以运行我们的第一个基于Kafka Streams的java应用程序。
我们将运行WordCount演示应用程序,它使用了ApacheKafka。它实现了WordCount算法,从输入文本来计算直方图。然而和其他你之前见过的操作被绑定在数据上的WordCount实例程序不同的是,这个示例程序是数据无界,无限流动的。和有界算法的变体类似,他是一个有状态的算法,跟踪并更新word的计数器。然后因为它必须接受无界的输入数据,它会周期性低输出其当前状态和计算结果,同时继续处理更多的数据,因为它不知道是否已经处理了所有的数据。这就是他和Hadoop 的Mapreduce算法之间的典型差异。一旦我们了解这种差异,检查了实际的输出数据之后,会更容易接受它。
由于wordCount示例程序与kafka打包在一起,已经在Kafka的Broker中集成,这就意味着我们不需要做额外的事情就可以运行它,无需编译任何Java源代码。
# Run the WordCount demo application. There won't be any STDOUT output.
# You can safely ignore any WARN log messages.
$ ./bin/kafka-run-class org.apache.kafka.streams.examples.wordcount.WordCountDemo
注意,这里没有魔术式的部署,实际上,使用kafkaStreams库中的任何应用程序,就像启动任何普通的Java应用程序,该脚本kafka-run-class也只是一个简单的java -cp命令的包装。
该WordCount示例程序将从输入topic中读取数据,然后计算wordCount,将计算结果不断进行输出。演示将运行几秒钟,然后和其他典型流处理应用程序不同的是,它将会自动终止。
在本章节中,我们将使用内置的命令行工具从kafka中手工读取数据。在实际使用中,你可以通过其他方式,通过Kakfa客户端从Kafka中读取数据。比如,如果你可以在自己的应用程序中使用Kafka客户端将数据从Kakfa中迁移到其它数据系统。
现在,我们可以从kafka输出topic中读取数据并检查wordcount实例运行结果。
./bin/kafka-console-consumer --zookeeper localhost:2181 \
--topic streams-wordcount-output \
--from-beginning \
--formatter kafka.tools.DefaultMessageFormatter \
--property print.key=true\
--property key.deserializer=org.apache.kafka.common.serialization.StringDeserializer \
--property value.deserializer=org.apache.kafka.common.serialization.LongDeserializer
Wordcount的数据将会被打印在如下的控制台中:
all 1
streams 1
lead 1
to 1
kafka 1
hello 1
kafka 2
streams 2
join 1
kafka 3
summit 1
这里,第一列是Kafka消息的key的字符串格式,第二列是消息的值,long类型。你可以通过Ctrl+c命令来终止控制台输出。
但是等一下,输出看起来是不是很奇怪?为什么会出现重复的条目?比如streams出现了两次:
# Why not this, you may ask?
all 1
lead 1
to 1
hello 1
streams 2
join 1
kafka 3
summit 1
对于上面的输出的解释是,wordCount应用程序的输出实际上是持续更新的流,其中每行记录是一个单一的word(即Message Key,比如Kafka)的计数。对于同一个Key的多个记录,么个记录之后是前一个的更新。
下面的两个图说明了在输出之后发生了什么。第一列显示KTable
首先,文本行”所有到kafka的流”正在处理中,每个新Table项中的新单词结果正在被构建成KTable对象(绿色高亮显示部分),并且相应的变化结果会被发送到下游KStream。
当第二个文本航的hello kafkastreams被处理的时候,我们观察到,相对第一次,已经存在的条目KTable被更新了(Kafak和Streams这两个单词). 修改后的记录被在此发送到了KStream。
这就解释了上述KStream第二列中显示的信息,为什么输出的topic上显示的内容,因为它是包含了变化的完整内容
all 1
streams 1
lead 1
to 1
kafka 1
hello 1
kafka 2
streams 2
join 1
kafka 3
summit 1
下面的展望超出了这个例子的范围。Kafka Strems使用了KTable和changlog的KStream,利用changlog Stream和KTable之间的二元性,你可以发布KStream表中从开始到结束的完整changelog,这样就可以重建KTable的内容。
一旦完成了快速入门,你可以按照以下顺序关闭Kafka集群。
1、 在它运行的终端中,使用Ctrl+c来停止KafkaBroker,或者杀死该进程。
2、 在其他终端,使用Ctrl+C停止Zookeeper实例或者杀死该进程
恭喜你,你已经运行了Kafka Streams的第一个应用程序并且将数据保存在了一个单节点的kafka集群中。Yeah!
至于下一步,我们会建议你:
1、 阅读KafkaStreams架构,了解其主要概念和设计原则。
2、 深入阅读KafkaStreams开发指南,这里包含了kafka Streams的DSL等各种文档。这些将会帮助你编写Kafka Streams的第一个应用程序。
处理Kafka Streams,你可能对下面这些也感兴趣:
1、 kafka Connect工具,在kakfa和其他数据系统必须Hadoop中迁移数据。
2、 从Kafka Client中读取和写入数据到你自己的应用程序当中。
################################################################################################
在本机中,我们会总结Kafka Streams的关键概念,如果要了解更多详细信息,请参考架构和开发指南。
在设计上,经过深思熟虑的设计,KafkaStreams和Apache Kafka紧密集成,它使用kafka
作为其内部消息层。因此,熟悉Kafka的关键概念也很重要,特别是第一章入门和第四章架构设计。你特别应该明白:
l 角色:Kafka区分生产者(Producer),消费者(Consumer)和Brokers。Producer发布数据到Kafka Brokers,Consumer从Kafka Brokers中读取发布的数据。Producers和Consumers是完全分离的。一个Kafka集群由一个或者多个Broker组成。
l 数据:数据被存储在Kafka的Topics中。Topic是Kafka提供的最重要的抽象,它是Producer发布的同一类数据的抽象统称。在Kakfa中,每个Topic可以分为一个或者多个分区,这些分区分布在各个Brokers上面用于容错。
l 并发度:Kafka的分区数或者是给定的topic的数量是确定Kafka数据读取和写入的并发度的主要因素。由于Kafka Streams和kafka紧密集成,所以kafka Streams的并发度会在很大程度上收到Kafka并发度的影响。
Stream是KafkaStreams提供的最重要的抽象。它代表的是无限的,不断更新的数据集,其中无限意味着大小不明确或者无大小限制。一个Stream是一个有序的,允许重放的不可变的数据记录。其中数据记录被定义为一个容错的键值对。
一个流处理应用程序可以是任何集成了Kafka Streams库的程序。在实际使用中,它很有可能就是你的Java应用程序。他可以通过由一个或者多个Processor组成的拓扑结构来定义其计算逻辑。
处理拓扑定义了由流处理应用程序进行数据处理的计算逻辑。拓扑是由流(边)连接的流处理器的图。开发者可以通过Low-level API或者通过Kafka Streams的DSL来构建拓扑。
第四章的架构设计文档对于拓扑有更详细的描述。
流处理器用来处理拓扑中的各个节点。它代表拓扑中的每个处理步骤,用来完成数据转换功能。Kafka Streams为流处理器提供了标准操作例如Map,Filte和聚合等一系列开箱即用的功能。一个流处理器同一时间从上游接收一条输入数据,然后处理这条数据,产生一个或者多个输出记录到下个流处理器。
KafkaStreams提供了两种方法来定义流处理器:
KafkaStreams DSL API. 最常用的转换操作比如map和Filter,这样你就不必从头开始实现这些流处理器。
Low-levelAPI:这些低级API允许开发人员定义和连接定制处理器和状态存储器进行交换。
流处理的一个很关键的方面就是时间,以及它是如何进行建模和整合的。比如,一些像Windows这样的窗口操作就是基于时间的界限所定义的。
流处理上时间相关的概念主要如下:
例如:如果事件产生于汽车GPS传感器报道的位置变化,那么相关的事件时间就是GPS传感器捕捉位置变化的时间。(也就是说,这个时间通常是在流处理系统以外产生的。)
例如:假设有一个应用程序用来读取和处理来自汽车GPS传感器报告的地理位置数据,并将其呈现给车队管理仪表盘。在这里,应用程序中的处理事件可能是毫秒或者秒(例如基于Apache的Kafka 和Kafka Stream流实时管道)或者小时(例如基于 Apache Hadoop或者Apache Spark的管道)。
事件时间和摄取时间的选择是通过在Kafka(不是KafkaStreams)上进行配置实现的。从Kafka 0.10.X起,时间戳会被自动嵌入到Kafka的Message中,可以根据配置选择事件时间或者摄取时间。配置可以在broker或者topic中指定。Kafka Streams默认提供的时间抽取器会将这些嵌入的时间戳恢复原样。因此,应用程序的有效时间语义上依赖于这种嵌入时时间戳读取的配置。请参考:Developer Guide
Kafka Streams通过时间抽取器为每个记录分配时间戳。Sreams中每个记录的时间戳(可能是乱序)可以进行时间相关的操作,比如Join,也可以用于相同应用中的多个输入数据流同步。
时间提取器的具体实现方式主要是检索或者计算两种方式。可以根据数据记录的实际内容获取时间戳,也可以使用其他方式,比如本地时间戳。开发人员可以根据自己的业务需求选择不同的实现方式。
注意:Kafka Streams中的摄取时间和其他流处理系统略有不同,其他流处理系统中的摄取时间指的是从数据源中获取到数据的时间,而kafka Streams中,摄取时间是指记录被追加到Kakfa topic中的时间。
提示:使用时间是,你也应该保证时区和日历的其他方面是正确同步的,或者至少是了解之间的差异。例如,统一使用UTC或者Unix时间,指定时间信息格式一致,如统一精确到秒,你也不要混用不同语义的时间。
一些流处理应用不需要状态,这意味着每个消息的处理都是彼此独立的。如果你只需要进行消息转换,或者基于某些条件进行筛选,那么你的流处理应用程序的拓扑会非常简单。
然而,能够保存状态,就是的流处理系统可以应用在更多场景。你可以进行Join,Group by或者aggregate操作,Kafka Streams DSL供了很多这样的包含状态的DSL。
在我们讨论Kafka Stream的aggregate等操作之前,我们必须先介绍表,以及最重要的表和流之间的关系,即流和表的双重性。本质上,这种双重性意味着一个流可以作为表,反之亦然。Kafka的Log compact功能,就是利用了这种双重性。
表的简单形式是一个KV对的集合,也可以称为Map或者关联数组。
说明:为了使得这一章节简答,我们会跳过复合键,MultiSet等的讨论。
有如下一张表:
下面这张Stream-Table描述了流和表之间双重性的密切关系。
Stream as table:流可以被认为是一张表,其中,流上的每条数据记录代表了表的状态变化的更新日志。一个流就是一张变相的表,它可以通过重建日志的方式很容易就变成一个真正的表。同样,一个更具普适性的例子– 例如从网页事件流中统计用户网页浏览的总数– 返回一张表(这里用Key和Value分别表示用户和其浏览量的总数。)
Table as Stream: 一个表可以被认为是流上一个时间点的快照。表上的每行记录,都代表该键的最新值。因此,一个表示一种变相的流,它可以通过遍历表中的每个KV对很容易变成一个真正的流。
让我们通过如下的例子进行说明:假设有一张跟踪用户浏览网页总数的统计表,随着时间的推移,当处理新的网页浏览记录的时候,表中的状态会被相应的更新,这里,不同时间点会有不同的变化,这种变化可以表示为一个changlog的流。
有意思的是,由于流和表的双重性,使用相同的流可以重构原始表(第三列)。
相同的机制还被用于如下场景,例如,数据库之间通过数据捕获机制(CDC change data capture)进行复制,Kafka Streams通过在多个节点内复制存储的状态来保证其容错性。流和 表的双重性的概念在Kafka Streams中极其重要,接下来的章节,我们将重点说明这些。
说明:只有Kafka Streams的DSL才有KStream的概念。
一个KStream是一个事件流,其中每条事件记录代表了无限的包含数据的数据集的抽象。用表来解释流的概念,数据的记录始终被解释为Insert,只有追加,因为没有办法替换当前已经存在的相同key的行数据。比如戏弄卡交易,一个页面视图或者服务器日志。
为了说明这一点,我们可以想象有如下两个记录被发送到流。
("alice", 1) -->("alice", 3)
如果你的流处理一弄程序是要汇总每个用户的键值,他就会返回4,因为第二个记录不会被视为当前记录的更新。如果是要比较使用最新的Key值,那么alice就会返回3.
说明:只有Kafka Streams的DSL才有KTable的概念。
一个KTable是一个changlog更新日志流。其中每个数据记录代表一个更新的抽象。准确来说,一个数据记录的值被认为是相同Key的最后一个值的更新结果。对于该表在流上的解释,可以被认为是一个更新,因为任何相同Key值的记录都会被更新。
为了说明这一点,我们可以假设有以下两个数据记录被发送到流。
("alice", 1) -->("alice", 3)
如果你的流处理应用程序要总结每个用户的总值,它会为alice返回3,因为第二个记录会被视为记录的更新。而KStream会将这种行为返回4.
说明:Kafka日志压缩的影响, 考虑KStream和KTable的另一种形式,如果一个KTable存储到Kafka的topic中,你就需要启用Kafka的日志压缩功能以节省空间。
然而,这种方式在KStream的情况下是不安全的,因为,一旦开启日志压缩,Kafka就会删除比较旧的Key值,这样就会破坏数据的语义。以数据重放为例,你会突然得到一个值为3的alice,而不是4,因为以前的记录都被日志压缩功能删除了。因此,日志压缩在KTable中使用是安全的,但是在KStream中使用是错误的。
我们已经看到了流和表的双重性在changlog中的例子,另外一个例子是关系型数据库中的捕获数据更新日志(CDC changelog data capture)记录,表示该行在数据库中的插入,更新或者删除。
KTable还提供了通过key查找数据值得功能,该查找功能可以用在Join等功能上。
一个流处理器可能需要将数据划分为多个时间段,这就是流上窗口。这通常在Join或者aggregation聚合等保存本地状态的处理程序中使用。
Kafka StreamsDSL API提供了可用的窗口操作,用户可以指定数据在窗口中的保存期限。这就允许Kafka Streams在窗口中保留一段时间的旧数据以等待其它晚到的数据。如果保留期过了之后数据才到达,这条消息就不能被处理,会被丢掉。
实时的流处理系统中,数据乱序总是存在的,这主要取决于数据在有效时间内如何进行处理。对于在正处于处理期的时间内的数据,如果数据乱序,延迟到达,在语义上就可以被正常处理,如果数据到达时候,已经不在处理期,那么这种数据就不适合处理期的语义,只能被丢弃掉。
Join操作负责在Key上对两个流的记录进行合并,并产生新流。一个基于流上的Join通常是基于窗口的,否则所有数据就都会被保存,记录就会无限增长。
KafkaStreams DSL支持不同的Join操作,比如KStram和KStream之间的Join,以及KStream和KTable之间的Join。
聚合操作需要一个输入流,并且以多个输入记录为单位组合成单个记录并产生新流。常见的聚合操作有count和sum。流上的聚合也必须基于窗口进行,否则数据和join一样都会无限制增长。
在Kafka Streams的DSL中,一个聚合输入流可以是KStream形式或者KTable形式,但是输出流永远都是KTable。这就使得Kafka Streams的输出结果会被不断更新,这样,当有数据乱序到达之后,数据也可以被及时更新,因为最终输出是KTable,新key会覆盖旧值。
###############################
转自:https://blog.csdn.net/ransom0512/article/details/52038548