Apache Kafka Streams和实时处理

1. 什么是Kafka Streams

Kafka  Streams是一个客户端库,用于处理和分析存储在Kafka中的数据。它建立在重要的流处理(可以连续,同时,逐个记录地实时处理数据的过程)概念之上,例如正确区分事件时间和处理时间,窗口支持,一次性处理语义和简单而有效的应用程序状态管理。凭借Kafka服务器端集群技术的优势,Kafka Streams在客户端让编写和部署标准JavaScala应用程序变的简单。

Kafka Streams的入门门槛低:你可以在一台机器上快速编写和运行小规模的程序示例,你也可以在多台计算机上运行应用程序的其他实例,以扩展到大容量的生产负载。Kafka Streams通过利用Kafka的并行模型,透明地处理同一应用程序的多个实例的负载平衡。

一些重要的特征:

  • 设计为简单轻量级的客户端库,可轻松嵌入任何Java应用程序,并能与任何用户为其流应用程序开发的已打包、部署并可操作的工具集成。

  • 作为内部消息传递层对Apache Kafka以外的系统没有外部依赖关系;值得注意的是,它使用Kafka的分区模型来水平扩展处理,同时保持强大的排序保证。

  • 支持容错本地状态,可实现非常快速有效的有状态操作,如窗口连接和聚合。

  • 支持完全一次处理语义,以保证每个记录只处理一次,即使在处理过程中流客户端或Kafka代理发生故障时也只处理一次。

  • 采用一次处理一条记录的方式以实现毫秒处理延迟,并支持基于事件时间的窗口操作以及记录的延迟到达。

  • 提供必要的流处理原语,以及高级别的Streams DSL和低级别的处理器API

2. Kafka  Streams处理拓扑(Streams Processing Topology

  • 流是Kafka Streams提供的最重要的抽象:它代表一个无限的,不断更新的数据集。流是有序,可重放和容错的不可变数据记录序列,其中数据记录被定义为键值对。

  • 流处理应用程序是使用Kafka Streams库的任何程序。它通过一个或多个处理器拓扑定义其计算逻辑,其中处理器拓扑是通过流(边缘)连接的流处理器(节点)的图。

  • 流处理器是处理器拓扑中的节点; 它表示通过从拓扑中的上游处理器一次接收一个输入记录然后转换流中的数据的处理步骤,并且随后可以向其下游处理器产生一个或多个输出记录。

拓扑中有两种特殊的处理器:

  • 源处理器Source Processor):源处理器是一种特殊类型的流处理器,它没有任何上游处理器。它消费一个或多个主题的记录并为拓扑生成一个输入流,然后将它们转发到其下游处理器。

  • 接收处理器Sink Processor):接收处理器是一种特殊类型的流处理器,它没有下游处理器。它把从其上游处理器接收的任何记录发送到指定的Kafka主题。

请注意,在正常处理器节点中,还可以在处理当前记录时访问其他远程系统。因此,处理后的结果可以流式传输回Kafka或写入外部系统。

pic

3. Kafka Streams架构

Kafka StreamsKafka生产者和消费者库上构建,并利用Kafka的自有功能来提供数据并行性,分布式协调,容错和操作简便性,从而简化了应用程序开发。

下图显示了使用Kafka Streams库的应用程序的解剖结构:

pic

a. 流分区和任务

Kafka的消息传递层对数据进行分区以存储和传输它,Kafka Streams对数据进行分区以进行处理。在这两种情况下,分区都可以实现数据的局部性,弹性,可伸缩性,高性能和容错性。Kafka Streams使用分区和任务的概念作为基于Kafka主题分区的并行模型的逻辑单元。 在并行化的背景下,Kafka StreamsKafka之间有着密切的联系:

  • 每个流分区都是完全有序的数据记录序列,并映射到一个Kafka主题分区。

  • 流中的一个数据记录映射到该主题的一个Kafka消息。

  • 数据记录的键值决定了KafkaKafka Streams中数据的分区,即数据如何路由到主题内的特定分区。

应用程序的处理器拓扑通过将其分解为多个任务来扩展。更具体地说,Kafka Streams基于应用程序的输入流分区创建固定数量的任务,每个任务被分配一些输入流的分区(即,Kafka主题)。分区到任务的分配永远不会改变,因此每个任务都是应用程序的固定并行单元,然后,任务可以根据分配的分区实例化自己的处理器拓扑;它们还为每个分配的分区维护一个缓冲区,并从这些记录缓冲区一次一个地处理消息。 因此,流任务可以独立并行地处理,无需人工干预。

重要的一点是要知道Kafka Streams不是资源管理器,而是一个库,这个库可以让它的流处理应用程序在任何地方运行。应用程序的多个实例在同一台机器上执行,或分布在多台机器上,并且库可以自动将任务分发给正在运行的应用程序实例。分区到任务的分配永远不会改变;如果应用程序实例失败,那么所有已分配给它的任务将在其他实例上自动重新启动,并继续使用相同的流分区。

下图显示了两个任务,每个任务分配了一个输入流分区:

pic

b. 线程模型 

Kafka Streams允许用户在一个应用程序实例中配置库(library)用来并行处理的线程数。每个线程可以独立地使用其处理器拓扑执行一个或多个任务。例如,下图显示了一个运行两个流任务的流线程。

pic

启动更多流线程或应用程序的更多实例仅仅是复制拓扑并使其处理Kafka分区的不同子集,从而能够实现有效地并行处理。值得注意的是,线程之间没有共享状态,因此不需要线程间协调,这使得跨应用程序实例、跨线程并行运行拓扑变得非常简单。Kafka Streams利用Kafka的协调功能透明地处理Kafka主题分区在各种流线程中的分配。

如上所述,使用Kafka Streams扩展流处理应用程序非常简单:只需要多启动一些应用程序的实例,Kafka Streams负责在应用程序实例中运行的任务之间分配分区。你可以启动与Kafka主题分区一样多的应用程序线程,以便在应用程序的所有正在运行的实例中,每个线程(或者更确切地说,它运行的任务)至少有一个要处理的输入分区。

c. 本地状态存储

Kafka Streams提供所谓的状态存储,流处理应用程序可以使用它来存储和查询数据,这是实现有状态操作时的一项重要功能。例如,当你调用有状态运算符(如join()aggregate())或窗口化流时,Kafka Streams DSL会自动创建和管理此类状态存储。

Kafka Streams应用程序中的每个流任务可以嵌入一个或多个可通过API访问的本地状态存储,以存储和查询处理所需的数据。 Kafka Streams为这些本地状态存储提供容错和自动恢复功能。

下图显示了两个具有专用本地状态存储的流任务:

pic

d. 容错

Kafka Streams构建在Kafka本身的容错功能之上。Kafka分区具有高可用性和复制性;因此,当流数据持久保存到Kafka时,即使应用程序失败并需要重新处理这些数据仍然可用。 Kafka Streams中的任务利用Kafka消费者客户端提供的容错功能来处理故障,如果任务所在的计算机故障了,Kafka Streams会自动在其余一个正在运行的应用程序实例中重新启动该任务。

此外,Kafka Streams还确保本地状态存储有很强的健壮性。对于每个状态存储,Kafka Streams维护一个复制的记录更改日志的Kafka主题,在该主题中跟踪所有的状态更新。这些更改日志主题也是分区的,因此每个本地状态存储实例以及访问存储的任务都有自己的专用更改日志主题分区。 在更改日志主题上启用了日志压缩,以便可以安全地清除旧数据,以防止主题的大小无限增长。如果任务在一台计算机上运行失败并在另一台计算机上重新启动,Kafka Streams会保证在处理新启动的任务之前,通过重放相应的更改日志主题,将其关联的状态存储恢复到故障之前的内容。因此,故障处理对最终用户完全透明。

4. Kafka Streams的实现示例

// 配置
Properties streamsConfiguration = new Properties();
streamsConfiguration.put(StreamsConfig.APPLICATION_ID_CONFIG, “Streaming-QuickStart”);
streamsConfiguration.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, “localhost:9092”);
streamsConfiguration.put(StreamsConfig.KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
streamsConfiguration.put(StreamsConfig.VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
 
// 得到主题和Serde
String topic = configReader.getKStreamTopic();
String producerTopic = configReader.getKafkaTopic();
final Serde stringSerde = Serdes.String();
final Serde longSerde = Serdes.Long();
 
// 构造流并获取数据
KStreamBuilder builder = new KStreamBuilder();
KStream<String, String> inputStreamData = builder.stream(stringSerde, stringSerde, producerTopic);
 
// 处理流
KStream<String, Long> processedStream = inputStreamData.mapValues(record -> record.length() )

除了连接和聚合操作之外,还为KStream提供了的其他转换的操作。因此,这些操作中的每一个都可以生成一个或多个KStream对象。此外,可以将一个或多个连接的处理器转换为底层处理器拓扑。为了组成复杂的处理器拓扑,所有这些转换方法可以链接在一起。

在这些转换中,filtermapmapValues等是无状态转换操作,用户可以使用它们将自定义函数作为参数传递,例如过滤器的谓词,地图的KeyValueMapper等。

// 把流写回Kafka
processedStream.to(stringSerde, longSerde, topic);

这里,即使在内部结构初始化之后,处理也不会开始。因此,通过调用start()方法,我们必须显式启动Kafka Streams线程:

KafkaStreams streams = new KafkaStreams(builder, streamsConfiguration);
streams.start();

最后一步是关闭流。

5. Kafka流的特征

  • 最佳功能是弹性,高可扩展性和容错性。

  • 可以部署到容器,虚拟机,裸机,云。

  • 对于小型,中型和大型用例,它同样可行。

  • 它完全与Kafka安全集成。

  • 可以编写标准Java应用程序。

  • 完全一次处理语义。

  • 不需要单独的处理集群。

  • 可以在MacLinuxWindows上开发。

6. Kafka Streams用例

a. The New York Times

The New York Times将发布的内容实时存储并分发给各种应用程序和系统,使其可供读者使用

b. Zalando

Zalando是欧洲领先的在线时尚零售商,它使用Kafka作为ESB(企业服务总线),这有助于他们从单一服务架构转变为微服务架构。此外,通过使用Kafka处理事件流,他们的技术团队可以实现近乎实时的商业智能。

c. LINE

为了相互通信,LINE使用Apache Kafka作为其服务的中央数据中心。Line每天产生数千亿条消息,用于执行各种业务逻辑,威胁检测,搜索索引和数据分析。此外,Kafka帮助LINE可靠地转换和过滤主题,使子主题的消费者能够有效的消费同时保持易于维护。

d. Printerest

Pinterest大规模使用Apache KafkaKafka Streams为其广告基础设施的实时预测预算系统提供支。Kafka Streams让花费预测比以往任何时候都更准确。

e. Rabobank

Apache Kafka支持数字神经系统 – Rabobank的业务事件总线。Rabobank是荷兰三大银行之一。通过使用Kafka Streams,该系统可以实时向客户发出金融事件的警报。

Apache Kafka Streams和实时处理

发表评论

电子邮件地址不会被公开。 必填项已用*标注

十七 − 九 =