4

Kafka入门实战教程(7):Kafka Streams - EdisonZhou

 2 years ago
source link: https://www.cnblogs.com/edisonchou/p/kafka_study_notes_part7.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1 关于流处理

流处理平台(Streaming Systems)是处理无限数据集(Unbounded Dataset)的数据处理引擎,而流处理是与批处理(Batch Processing)相对应的。所谓的无线数据,指的是数据永远没有尽头。而流处理平台就是专门处理这种数据集的系统或框架。下图生动形象地展示了流处理和批处理的区别:

381412-20220718195218542-960138191.png

总体来说,流处理给人的印象是低延时,但是结果可能不太精确。而批处理则相反,它能提供精确的结果,但是往往存在高时延。

一个最简单的Streaming的结构如下图所示:

381412-20220718195241375-1635474833.png

从一个Topic中读取到数据,经过一些处理操作之后,写入到另一个Topic中,嗯,这就是一个最简单的Streaming流式计算。其中,Source Topic中的数据会源源不断的产生新数据。

那么,我们再在上面的结构之上扩展一下,假设定义了多个Source Topic及Destination Topic,那就构成如下图所示的较为复杂的拓扑结构:

381412-20220718195250250-1193191246.png

2 关于Kafka Streams

近些年来,开源流处理领域涌现出了很多优秀框架。光是在 Apache 基金会孵化的项目,关于流处理的大数据框架就有十几个之多,比如早期的 Apache Samza、Apache Storm,以及这些年火爆的 Spark 以及 Flink 等。

Kafka Streams的特点

相比于其他流处理平台,Kafka Streams 最大的特色就是它不是一个平台,至少它不是一个具备完整功能(Full-Fledged)的平台,比如其他框架中自带的调度器和资源管理器,就是 Kafka Streams 不提供的。Kafka 官网明确定义 Kafka Streams 是一个客户端库(Client Library)。我们可以使用这个库来构建高伸缩性、高弹性、高容错性的分布式应用以及微服务。使用Kafka Streams API构建的应用程序就是一个普通的应用程序,我们可以选择任何熟悉的技术或框架对其进行编译、打包、部署和上线。很不幸,目前Kafka Streams还没有在除了Java之外的其他主流开发语言的SDK上提供。Kafka Streams最大的特点就是,对于上下游数据源的限定。目前Kafka Streams只支持与Kafka集群进行交互,它并没有提供开箱即用的外部数据源连接器。

381412-20220718224701550-309491083.png

Kafka Streams应用执行

Kafka Streams宣称自己实现了精确一次处理语义(Exactly Once Semantics, EOS,以下使用EOS简称),所谓EOS,是指消息或事件对应用状态的影响有且只有一次。其实,对于Kafka Streams而言,它天然支持端到端的EOS,因为它本来就是和Kafka紧密相连的。下图展示了一个典型的Kafka Streams应用的执行逻辑:

381412-20220718224743368-1336211811.png

通常情况下,一个 Kafka Streams 需要执行 5 个步骤:

  • 读取最新处理的消息位移;

  • 读取消息数据;

  • 执行处理逻辑;

  • 将处理结果写回到 Kafka;

  • 保存位置信息。

这五步的执行必须是原子性的,否则无法实现精确一次处理语义。而在设计上,Kafka Streams在底层大量使用了Kafka事务机制和幂等性Producer来实现多分区的写入,又因为它只能读写Kafka,因此Kafka Streams很easy地就实现了端到端的EOS。

3 Kafka Streams客户端

目前.NET圈主流的Kafka客户端Confluent.Kafka并没有提供Streams的功能,其实,目前Kafka Streams也只在Java客户端提供了Streams功能,其他语言均没有提供。

381412-20220718224927962-318877194.png

画外音:毕竟Kafka是JVM系语言写的(Scala+Java),Java就是嫡系,一等公民。

那么,Confluent.Kafka团队有没有计划提供这个功能呢?我在issue列表找到了一些comments,得到的结果是目前没有这个计划,它涉及到太多的工作量,WTF。那么,.NET就真的没有可以用的Kafka Streams客户端了么?实际上,有的,我在Confluent.Kafka的issue内容中找到了下面这个Kafka Streams客户端:Streamiz.Kafka.Net。

381412-20220718224936756-1219857891.png

Streamiz.Kafka.Net:https://github.com/LGouellec/kafka-streams-dotnet

目前Streamiz.Kafka.Net这个项目仍然属于一个不断开发完善的阶段,Star数量278个,生产环境估计无法直接使用,但是拿来学习实践还是可以的,目前最新版本:1.3.0。其实,Streamiz.Kafka.Net也是基于Confluent.Kafka开发的,相当于对Confluent.Kafka做了一些DSL扩展。它的接口名字与用法,和Java API几乎一致。

4 第一个Streaming应用

如果你对Streaming的概念还不了解,建议先阅读上一篇文章。

应用程序部分

首先,创建一个.NET Core或.NET 5/6的控制台应用程序。

然后,通过Nuget安装Streamiz.Kafka.Net包:

PM>Install-Package Streamiz.Kafka.Net

然后,开始编写第一个Streaming应用程序:

using Streamiz.Kafka.Net;
using Streamiz.Kafka.Net.SerDes;
using Streamiz.Kafka.Net.Stream;
using Streamiz.Kafka.Net.Table;
using System;
using System.Threading.Tasks;

namespace EDT.Kafka.Streams.Demo
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            // Stream configuration
            var config = new StreamConfig<StringSerDes, StringSerDes>();
            config.ApplicationId = "test-streams-app";
            config.BootstrapServers = "kafka1:9091,kafka2:9092,kafka3:9093";

            StreamBuilder builder = new StreamBuilder();

            // Stream "test-stream-input" topic with filterNot condition and persist in "test-stream-output" topic.
            builder.Stream<string, string>("test-stream-input")
                .FilterNot((k, v) => v.Contains("test"))
                .To("test-stream-output");

            // Create a table with "test-ktable" topic, and materialize this with in memory store named "test-store"
            builder.Table("test-stream-ktable", InMemory<string, string>.As("test-stream-store"));

            // Build topology
            Topology t = builder.Build();

            // Create a stream instance with toology and configuration
            KafkaStream stream = new KafkaStream(t, config);

            // Subscribe CTRL + C to quit stream application
            Console.CancelKeyPress += (o, e) =>
            {
                stream.Dispose();
            };

            // Start stream instance with cancellable token
            await stream.StartAsync();
        }
    }
}

这个示例Streaming应用程序很简单,它实现的就是一个如下图所示的最简单的处理流程:

381412-20220718225136151-268492770.png

Source Topic是test-stream-input,Destination Topic是test-stream-output,分别对应输入源 和 输出地。在处理过程中会创建一个Table,名为test-stream-ktable,它会作为输入流和输出流的中间状态。在Kafka Streams中,流在时间维度上聚合成表,而表在时间维度上不断更新成流。换句话说,表会转换成流,流又再转换成表,如此反复,完成所谓的Streaming流式计算。

381412-20220718225157037-1350061492.png

这个test-stream-ktable会存储在内存中一个名为test-stream-kstore的区域,我们理解到这里就够了。最后,回到最关键的一句代码,如下所示。在对输入源进行处理时,使用了一个DSL进行快速的过滤,即判断输入的消息是否包含test这个字符串,包含就不做过滤处理,不包含则进行处理,即传递给test-stream-output。

最后,回到最关键的一句代码,如下所示。在对输入源进行处理时,使用了一个DSL进行快速的过滤,即判断输入的消息是否包含test这个字符串,包含就不做过滤处理,不包含则进行处理,即传递给test-stream-output。

builder.Stream<string, string>("test-stream-input")
   .FilterNot((k, v) => v.Contains("test"))
   .To("test-stream-output");

Broker部分

为了完成这个demo,我们提前在Kafka Broker端创建几个如下图红线框中的topic。

381412-20220718225325889-330902589.png

为了方便演示验证,我们暂且都给他们设置为单个分区,无额外副本。

测试效果

首先,我们将.NET控制台程序启动起来。

然后,我们在Broker端打开一个Producer命令行,陆续手动输入一些数据源:

# kafka-console-producer.sh --topic=test-stream-input --broker-list kafka1:9091,kafka2:9092,kafka3:9093
>haha
>test112321
>test123214214
>tesst^H^Ht
>test9898
>xifejlrkewl
>xjkfldsjoifdsfjods
>xjoijfosifjlkdsjflkds
>xjofdksjfljdslkfdsj
>xjlfjdslkjdslfjds
>xjlkdjflksjdlfks
>hello
>helloworld

 可以看到,输入的数据源中包含了3个含有test关键词的字符串消息。期望的结果是,在Streams应用程序处理逻辑中,过滤掉这3个,将其余的消息都进行处理传递到output中。

然后,我们就可以通过Kafka Tool去看看input和output这两个topic的数据验证一下了:

(1)test-stream-input

381412-20220718225416319-723587320.png

(2)test-stream-output

381412-20220718225423647-1410730500.png

可以看到,test-stream-output中未包含含有test关键词的消息,第一个Streaming应用程序运行成功。

5 经典WordCount应用

所谓wordcount就是一个经典的单词计数的应用程序,它可以统计在指定数据源中每个单词出现的次数。在Streaming流式计算和MapReduce分布式计算中,它经常出现在示例代码中。

381412-20220718225658558-209470178.jpg

应用程序部分

改写一下上面的demo实例代码:

var config = new StreamConfig<StringSerDes, StringSerDes>();
config.ApplicationId = "test-wordcount-app";
config.BootstrapServers = "kafka1:9091,kafka2:9092,kafka3:9093";

StreamBuilder builder = new StreamBuilder();

builder.Stream<string, string>("test-word-in")
    .FlatMapValues(value => value.Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList()) // 根据空格分隔多个单词
    .Map((key, value) => KeyValuePair.Create(value, "1")) // 转换为(单词, 1)的键值对形式
    .GroupByKey() // 根据单词分组
    .Count() // 计算各个分组value的数量
    .ToStream()
    .Map((key, value) => KeyValuePair.Create(key, $"{key} : {value.ToString()}"))
    .To("test-word-out");

// Create a table with "test-ktable" topic, and materialize this with in memory store named "test-store"
builder.Table("test-word-ktable", InMemory<string, string>.As("test-word-store"));

// Build topology
Topology t = builder.Build();

// Create a stream instance with toology and configuration
KafkaStream stream = new KafkaStream(t, config);

// Subscribe CTRL + C to quit stream application
Console.CancelKeyPress += (o, e) =>
{
    stream.Dispose();
};

// Start stream instance with cancellable token
await stream.StartAsync();

Broker端部分

新增几个示例代码需要用到的topic:test-word-in, test-word-out 以及 test-word-ktable。

测试效果

首先,我们将.NET控制台程序启动起来。

然后,我们在Broker端打开一个Producer命令行,陆续手动输入一些数据源:

# kafka-console-producer.sh --topic=test-word-in --broker-list kafka1:9091,kafka2:9092,kafka3:9093
>hello world
>hello jav^H
>hello csharp
>hello golang

可以看到,这里我们的hello出现了4次,其他单词均只出现了1次。

那么,我们可以直接去test-word-out这个topic中验证一下:

381412-20220718225809047-306110174.png

 本文总结了Kafka Streams的基本概念与执行流程,并结合.NET客户端给出了一个Kafka Streams应用程序的示例。

kafka-streams-dotnet:https://lgouellec.github.io/kafka-streams-dotnet

极客时间,胡夕《Kafka核心技术与实战》

B站,尚硅谷《Kafka 3.x入门到精通教程》

o_200902144330EdisonTalk-Footer.jpg

作者:周旭龙

出处:https://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK