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

总体来说,流处理给人的印象是低延时,但是结果可能不太精确。而批处理则相反,它能提供精确的结果,但是往往存在高时延。
一个最简单的Streaming的结构如下图所示:

从一个Topic中读取到数据,经过一些处理操作之后,写入到另一个Topic中,嗯,这就是一个最简单的Streaming流式计算。其中,Source Topic中的数据会源源不断的产生新数据。
那么,我们再在上面的结构之上扩展一下,假设定义了多个Source Topic及Destination Topic,那就构成如下图所示的较为复杂的拓扑结构:

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集群进行交互,它并没有提供开箱即用的外部数据源连接器。

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

通常情况下,一个 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功能,其他语言均没有提供。

画外音:毕竟Kafka是JVM系语言写的(Scala+Java),Java就是嫡系,一等公民。
那么,Confluent.Kafka团队有没有计划提供这个功能呢?我在issue列表找到了一些comments,得到的结果是目前没有这个计划,它涉及到太多的工作量,WTF。那么,.NET就真的没有可以用的Kafka Streams客户端了么?实际上,有的,我在Confluent.Kafka的issue内容中找到了下面这个Kafka Streams客户端:Streamiz.Kafka.Net。

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应用程序很简单,它实现的就是一个如下图所示的最简单的处理流程:

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

这个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。

为了方便演示验证,我们暂且都给他们设置为单个分区,无额外副本。
测试效果
首先,我们将.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

(2)test-stream-output

可以看到,test-stream-output中未包含含有test关键词的消息,第一个Streaming应用程序运行成功。
5 经典WordCount应用
所谓wordcount就是一个经典的单词计数的应用程序,它可以统计在指定数据源中每个单词出现的次数。在Streaming流式计算和MapReduce分布式计算中,它经常出现在示例代码中。

应用程序部分
改写一下上面的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中验证一下:

6 总结
本文总结了Kafka Streams的基本概念与执行流程,并结合.NET客户端给出了一个Kafka Streams应用程序的示例。
参考资料
kafka-streams-dotnet:https://lgouellec.github.io/kafka-streams-dotnet
极客时间,胡夕《Kafka核心技术与实战》
B站,尚硅谷《Kafka 3.x入门到精通教程》

Kafka入门实战教程(7):Kafka Streams的更多相关文章
- 转 Kafka入门经典教程
Kafka入门经典教程 http://www.aboutyun.com/thread-12882-1-1.html 问题导读 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic. ...
- Kafka入门经典教程
本帖最后由 desehawk 于 2015-5-3 00:45 编辑问题导读 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程 ...
- Kafka入门经典教程【转】
问题导读 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程序?4.数据传输的事务定义有哪三种?5.Kafka判断一个节点是否活着有 ...
- [入门帮助] Kafka入门经典教程
问题导读 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程序?4.数据传输的事务定义有哪三种?5.Kafka判断一个节点是否活着有 ...
- ZooKeeper入门实战教程(一)-介绍与核心概念
1.ZooKeeper介绍与核心概念1.1 简介ZooKeeper最为主要的使用场景,是作为分布式系统的分布式协同服务.在学习zookeeper之前,先要对分布式系统的概念有所了解,否则你将完全不知道 ...
- 《OD大数据实战》Kafka入门实例
官网: 参考文档: Kafka入门经典教程 Kafka工作原理详解 一.安装zookeeper 1. 下载zookeeper-3.4.5-cdh5.3.6.tar.gz 下载地址为: http://a ...
- Kafka入门教程(二)
转自:https://blog.csdn.net/yuan_xw/article/details/79188061 Kafka集群环境安装 相关下载 JDK要求1.8版本以上. JDK安装教程:htt ...
- kafka入门教程链接
http://www.aboutyun.com/forum.php?mod=viewthread&tid=12882 经典入门教程 1.Kafka独特设计在什么地方?2.Kafka如何搭建及创 ...
- kafka实战教程(python操作kafka),kafka配置文件详解
kafka实战教程(python操作kafka),kafka配置文件详解 应用往Kafka写数据的原因有很多:用户行为分析.日志存储.异步通信等.多样化的使用场景带来了多样化的需求:消息是否能丢失?是 ...
随机推荐
- NLP教程(4) - 句法分析与依存解析
作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www.showmeai.tech/article-det ...
- Linux 设置开机自启动脚本(ES、MySQL、Nacos、Nginx)
/etc/rc.d/init.d 中文件会在 Linux 系统各项服务都启动完毕之后再被运行 cd /etc/rc.d/init.d:新建xxx.sh文件. chmod +x xxx.sh,赋予可执行 ...
- Hive 3.x 配置&详解
Hive 1. 数据仓库概述 1.1 基本概念 数据仓库(英语:Data Warehouse,简称数仓.DW),是一个用于存储.分析.报告的数据系统. 数据仓库的目的是构建面向分析的集成化数据环境,分 ...
- HamsterBear Linux Low Res ADC按键驱动的适配 + LVGL button移植
HamsterBear lradc按键驱动的适配 平台 - F1C200s Linux版本 - 5.17.2 ADC按键 - 4 KEY tablet 驱动程序位于主线内核: drivers/inpu ...
- unity---寻路导航
寻路导航 1. 简单的寻路 先搭建出类似下面的结构 将你想作为障碍的物体放入一个空物体中 进入空物体点击Static,仅勾选 Navigation Static 即可 依次点击 Window-> ...
- ex_Lucas定理
Lucas定理(p为质数): \(C_n^m=C_{n/p}^{m/p}*C_{n\ mod\ p}^{m\ mod\ p}\) 可是p不为质数怎么办呢? ex_Lucas定理 (p不为质数) 思路 ...
- 背包,子集和以及 (max, +) 卷积在特殊情形下的求法
背包,子集和以及 (max, +) 卷积在特殊情形下的求法 子集和 1:总重量不太大 有 \(n\) 个物品,每个物品重量为 \(w_i\),且 \(\sum\limits_{i} w_i=C\).你 ...
- 最强肉坦:RUST多线程
Rust最近非常火,作为coder要早学早享受.本篇作为该博客第一篇学习Rust语言的文章,将通过一个在其他语言都比较常见的例子作为线索,引出Rust的一些重要理念或者说特性.这些特性都是令人心驰神往 ...
- go-zero 微服务实战系列(二、服务拆分)
微服务概述 微服务架构是一种架构风格,它将一个大的系统构建为多个微服务的集合,这些微服务是围绕业务功能构建的,服务关注单一的业务功能,这些服务具有以下特点: 高度可维护和可测试 松散的耦合 可独立部署 ...
- .NET中测试代码运行时间
更新记录 本文迁移自Panda666原博客,原发布时间:2021年6月29日. 计算代码运行的时间,除了呆萌地用秒表去计时,或者可以通过Visual Studio来查看,还可以在.NET代码中使用St ...