大家好。在这个项目中,我将演示如何使用Apache Kafka过滤流数据的过程。我们将原始数据存储在一个名为**raw_data**
的主题中,该主题包含来自10,000个用户和1,000,000个进程的数据。原始数据将通过三个Kafka代理服务器来消费和处理。
在此过程中,我们将使用Scala Spark获取原始数据,然后将其筛选成三个不同的类别:
- 余额少于500时,我们会把它发到
**low**
频道。 - 余额在500到2000之间时,我们会把它发到
**mid**
频道。 - 余额超过2000时,我们会把它发到
**high**
频道。
一旦流开始,我们将从**low**
、**mid**
和**high**
主题读取数据,使用Apache Druid。这些数据将在Apache Druid中进行合并。我将演示如何在Druid中创建一个新表,该表可以通过SQL查询。这将对需要数据分析的团队非常有帮助。
此外,我们将创建一个 Superset 数据仪表板。Apache Druid 在此过程中发挥着关键作用,它使我们能够高效地分析和可视化数据。
🛠️系统和软件版本: 节:或
章节:- Kafka KRaft 版本: 3 个 Broker.
- Provectus — Kafka-UI.
- Kafka-Schema-Registry.
- 用于原始数据集的数据生成器.
- Spark Master 和 Spark Worker.
- Scala Spark 脚本和实时流处理.
- Apache Druid.
- Superset.
附注:请检查我的 GitHub 项目中的
**docker-compose.yml**
、Dockerfile、数据生成脚本、Scala Spark 脚本/程序以及其他相关配置文件。
链接 =https://github.com/mcagriaktas/End-to-End-Streaming-Filter-Project/tree/main
在访问GitHub仓库时,请运行以下命令进行克隆:
git clone asdasdasdasd.git
这将克隆一个仓库到本地:
注意: 首先我会解释所有工具和过程。之后我会演示如何开始容器并运行项目。
- 如何启动容器
- 如何开始项目
我们将使用Kafka KRaft 3.8.0,在3个节点的集群中。所有相关文件都在**container**
文件夹里。
你也可以在以下文件夹里找到所有配置文件。
在这个场景中,我们的Kafka broker将使用PLAINTEXT相互连接。如果您需要使用SASL_PLAINTEXT和Kafka ACLs来实现安全功能,请访问我们的项目页面以获取更多详细信息。
ling: 参见《基于角色的访问控制在Apache Kafka上的实现——使用Provectus》https://medium.com/@mucagriaktas/rbac-on-apache-kafka-with-provectus-cf356c8a2ba7
我们将对Kafka进行极限测试,因此我们将创建一个具有3个分区的主题。因此,我按如下方式配置了**server.properties**
文件:
############################# 基本日志设置 #############################
# 一个用逗号分隔的列表,列出要存储日志文件的目录
log.dirs=/data/kafka
# 每个主题的默认日志分区数量。更多的分区可以提供更大的消费并行性,但这也会导致更多文件分布在代理之间。
num.partitions=3
# 每个数据目录在启动时用于日志恢复和关闭时用于刷新的线程数。
# 对于数据目录位于RAID阵列中的安装,建议增加此值。
num.recovery.threads.per.data.dir=3
############################# 内部主题设置 #############################
# 用于存储内部主题“__consumer_offsets”和“__transaction_state”的复制因子
# 除了开发测试之外,建议使用更大的值以确保高可用性,如3。
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3
transaction.state.log.min.isr=3
leader.imbalance.check.interval.seconds=300
此外,我们的Kafka代理需要同时连接到本地主机(localhost
)和彼此。因此,**listener**
和**advertised.listeners**
配置必须同时包括内部和外部的listener
和advertised.listeners
。
listeners=PLAINTEXT://0.0.0.0:9192,CONTROLLER://0.0.0.0:9093,LISTENER_DOCKER_EXTERNAL://0.0.0.0:19092
advertised.listeners=PLAINTEXT://kafka1:9192,LISTENER_DOCKER_EXTERNAL://localhost:19092
如果你想理解卡夫卡的逻辑,可以看看我写的另一篇文章:
ling:<https://medium.com/@mucagriaktas/单体架构与使用Apache Kafka的消息队列(消息总线)-55e8fd5030c8>
2. Provectus — Kafka-UI实际上,Kafka-UI 是一个非常简单易用的工具。我们只需要两个文件即可:一个用于我们的 JAR 文件,另一个叫做名为config.yml
的文件。你可以在这里查看这些文件,在containers > container-images > provectus > Dockerfile
中找到这些文件。
这个 JAR 文件就是我们的 Kafka-UI 应用程序。我们需要创建或准备一个 **config.yml**
配置文件,然后使用 **starter-kafka-ui.sh**
脚本来运行 JAR 文件并确保 **config.yml**
文件参与其中。
RUN wget https://github.com/provectus/kafka-ui/releases/download/v0.7.2/kafka-ui-api-v0.7.2.jar # 下载 kafka-ui-api-v0.7.2.jar 文件
在 config => provectus
文件夹中,你会看到 config.yml
文件。在这里,我们设置了 Kafka 的 IP 地址(我使用了容器名)。但你也可以直接使用 IP 地址,因为我为每个容器在 docker-compose.yml
中设置了一个单独的子网。
kafka:
clusters:
- name: cagri_cluster
bootstrapServers: kafka1:9192,kafka2:9192,kafka3:9192
server:
port: 18080
logging:
level:
root: INFO
**docker-compose.yml**
会通过 **.sh**
脚本启动 Provectus。
#!/bin/bash
java -Dspring.config.additional-location=/mnt/config.yml -jar /mnt/kafka-ui-api-v0.7.2.jar
Kafka-UI 提供了多种功能:您可以创建主题、发送和接收消息,还可以查看您的 Kafka Schema Registry。
3. Kafka 模式注册表部署 Kafka Schema Registry 十分简单 — Confluent 已将它做得非常用户友好。你可以查看 **containers**
文件夹中的部署说明。
# 创建 Schema Registry 安装目录的文件夹
RUN mkdir /opt/schema-registry && \
curl -SL "https://packages.confluent.io/archive/7.3/confluent-community-7.3.0.tar.gz" \
-o /opt/schema-registry/confluent.tar.gz && \
mkdir /schema-registry && cd /schema-registry && \
建立 /schema-registry 目录,切换到该目录并解压文件使用 tar -xvzf 命令解压文件并删除多余的目录层次 && \
然后删除下载的压缩文件
接着,**docker-compose.yml**
文件会通过调用 **start-schema-registry.sh**
脚本来启动我们运行的 Kafka Schema Registry 容器。同样,我们也需要使用一个 properties 文件来定义我们的 Kafka代理的连接配置。
kafkastore.bootstrap.servers=PLAINTEXT://kafka1:9192,PLAINTEXT://kafka2:9192,PLAINTEXT://kafka3:9192
配置行定义了Kafka集群中各个节点的地址,格式为PLAINTEXT://hostname:port
,表示使用明文协议连接到指定的Kafka节点。
之后,我们的start-schema-registry.sh就会启动模式注册器。
#!/bin/bash
# 启动注册表
/schema-registry/bin/schema-registry-start /schema-registry/etc/schema-registry/schema-registry.properties
一旦我们部署了容器和配置文件,就可以访问它们了。当我们把架构推送到注册表时,我们就能在提供的链接里看到它了。
kafka-schema-registry 为什么重要。
由于Kafka Schema Registry管理着模式定义,我们将为我们的**raw topic**
定义一个模式定义。当我们用Spark读取数据时,我们需要使用从Kafka Schema Registry中获取的模式来解析(也就是“展开”)数据。
另外,Kafka Schema Registry 还提供了:
- 模式版本控制:模式注册表管理不同版本的模式,允许你在保持兼容性的同时,随着时间的推移演进模式。你可以检索特定版本的模式,从而更轻松地处理数据结构的变更。
- 数据兼容性:模式注册表确保模式版本间的数据一致性。
- 集中式模式管理:通过为所有模式使用集中式注册表,你将拥有一个单一真实来源,简化跨不同微服务或数据处理管道的模式管理。
- 序列化和反序列化:模式注册表提供与Kafka客户端集成的序列化器和反序列化器,允许应用程序使用Avro、Protobuf或JSON模式格式。这简化了数据处理过程,并减少了生产者与消费者间序列化错误的发生。
在这个场景中,我的核心逻辑是处理流式数据,过滤它,然后将其插入特定的主题中。因此,我不需要真实的数据集。相反,通过Python的Faker模块,我创建了一个自动数据集。我们将数据以JSON格式插入到我们的原始主题中。
在 **datagenerator**
文件夹中,你会看到 Avro 和 JSON 格式的子文件夹。我们将使用 JSON 格式的数据。数据集相当大,所以请在你的机器上运行脚本生成自己的数据集。这样,当你部署 Kafka 集群时,你就可以从原始数据开始。
python create_dataset.py
运行创建数据集的Python脚本 (yùn háng chuàng jì shù jú de Python jiǎo běn)
如果您使用的是 16 GB 内存,也可以修改 create_dataset.py
脚本。
可以使用 **create_dataset.py**
脚本文件来生成自动数据集。
当我们的数据来自一个数据生成工具,例如模拟我们公司网页应用中10,000名用户的行为,我们需要有效地过滤这些数据。为此,我们正在使用相应的技术来处理大数据。为了处理负载,我设置了一个包含两个工作节点和一个主节点的架构。
要运行 **.jar**
文件,请切换到 **containers => config => spark => submitfiles**
目录。在这个文件夹里,你会找到两个重要的文件。
- scala-spark-submit.sh — 一个用于在 Spark 主容器中运行
**.jar**
文件的脚本文件。 - The
**.jar**
文件 — 我将在本文的第六部分解释如何在 Scala 中打包生成**.jar**
文件。
此外,文件夹中还有一个名为 **download_jar.sh**
的脚本。有时候 Spark-submit 无法自动下载依赖。这种情况下,你可以在 Maven 仓库 上找到所需的库,复制下载链接并使用 **download_jar.sh**
脚本获取它。
更多详情请参考我另外两篇文章
- https://medium.com/@mucagriaktas/end-to-end-data-engineer-data-lake-project-scala-spark-3-5-1-150246b65d1f - 终到终的数据工程师数据湖项目 Scala Spark 3.5.1 介绍
- https://medium.com/@mucagriaktas/apache-spark-hadoop-apache-spark-and-parquet-orc-format-d352bf95833 - Apache Spark、Hadoop 以及 Parquet ORC 格式的介绍
当我们从我们的**原始主题**
读取数据时,我们需要使用Kafka 架构注册表来处理我们Kafka主题中的数据,这就是我们包含Kafka 架构注册表容器的原因。你可以在**streaming => scala**
文件夹中查看完整的脚本。如上所示,我们已经在**localhost:18081**
设置了Kafka 架构注册表容器。
val schemaRegistryUrl = "定义 schemaRegistryUrl 为 http://kafka-schema-registry:18081"
val structSchema = "将结构模式 structSchema 设置为 fetchSchemaFromRegistry 函数的返回值,参数包括 spark,schemaRegistryUrl 和 'raw-value'"
def fetchSchemaFromRegistry(spark: SparkSession, schemaRegistryUrl: String, subject: String): StructType = {
"定义一个名为 fetchSchemaFromRegistry 的函数,它接受 SparkSession,字符串类型的 schemaRegistryUrl 和 subject 作为参数,并返回一个 StructType 类型的结果"
val url = "将 URL 设置为 schemaRegistryUrl 的连接加上 '/subjects/$subject/versions/latest'"
val response = "将响应内容 response 设置为从 URL 获取的字符串"
val json = Json.parse(response)
val schemaString = "将模式字符串 schemaString 设置为 json 中 'schema' 字段的字符串值"
val schemaJson = "解析模式字符串 schemaString 并将其转换为 schemaJson"
val fields = "将 schemaJson 中的 'fields' 字段转换为一个 JsObject 的序列,并映射到 fields 变量中"
(schemaJson \ "fields").as[Seq[JsObject]].map { field =>
val fieldName = (field \ "name").as[String]
val fieldType = "根据字段类型 field\ "type" 的值,将 fieldType 设置为相应的类型"
(field \ "type").as[String] match {
case "string" => "设置 fieldType 为 StringType"
case "double" => "设置 fieldType 为 DoubleType"
case _ => "设置 fieldType 为 StringType"
}
StructField(fieldName, fieldType, nullable = true)
}
"返回结构类型 StructType,其字段为 fields"
StructType(fields)
}
之后,我们配置了Spark和Kafka的JAR文件。您可以在https://mvnrepository.com/的Maven仓库页面中查看所需的依赖项。以下是我们的完整包需求。
// 定义Scala版本号
val scala2Version = "2.12.20"
// 定义Spark版本号
val sparkVersion = "3.5.3"
// 引入Spark核心库
"org.apache.spark" %% "spark-core" % sparkVersion,
// 引入Spark SQL库
"org.apache.spark" %% "spark-sql" % sparkVersion,
// 引入Spark流处理库
"org.apache.spark" %% "spark-streaming" % sparkVersion,
// 引入Spark Kafka 0.10流处理库
"org.apache.spark" %% "spark-streaming-kafka-0-10" % sparkVersion,
// 引入Spark SQL Kafka 0.10库
"org.apache.spark" %% "spark-sql-kafka-0-10" % sparkVersion,
// 引入Kafka客户端库
"org.apache.kafka" % "kafka-clients" % "3.5.1",
// 引入Play JSON库
"com.typesafe.play" %% "play-json" % "2.9.4"
我在脚本中设置了一个基本的过滤部分。你也可以修改过滤功能部分,利用Apache Spark进行自定义操作。
定义过滤数据函数 `filterData(data: DataFrame): (DataFrame, DataFrame, DataFrame)`,该函数接收一个 `DataFrame` 参数并返回三个 `DataFrame` 对象。具体逻辑如下:
- 将数据中 `balance` 小于等于 500 的记录筛选出来,存入 `lowTopic`。
- 将数据中 `balance` 大于 500 且小于等于 2000 的记录筛选出来,存入 `midTopic`。
- 将数据中 `balance` 大于 2000 的记录筛选出来,存入 `highTopic`。
最终返回这三个 `DataFrame` 对象 `(lowTopic, midTopic, highTopic)`。
我们可以详尽地讨论这些脚本,你也可以在网页上查看代码逻辑。在最后一步中,我们将通过流处理生成过滤后的数据。
def 写入Kafka(data: 数据框, topic: String): Unit = {
data选择表达式("将id转换为字符串 as 键", "转换为JSON(结构(*)) AS 值")
.写入流
.格式("kafka")
.选项("kafka.bootstrap.servers", "kafka1:9192,kafka2:9192,kafka3:9192")
.选项("主题", topic)
.选项("检查点位置", s"/tmp/checkpoints/$topic")
.输出模式("追加")
.启动()
}
顺便提一下,建议将代码拆分成函数,这样在生产环境中每个人都能更容易看懂。
如果你修改了脚本内容,你需要重新编译一下。使用命令:sbt clean package 来完成。
cagri@Dahbest:~/projects/streaming/streaming/scala/target$ sbt clean package
7. Apache Druid,
Apache Druid 处理流数据并允许你对其运行 SQL 查询。它既可以本地部署,也可以云端部署,并且可以与 Apache Kafka 无缝对接。Apache Druid 低延迟,并可以直接从 Kafka 获取数据。
部署 Apache Druid 时,需要配置环境。在 **druid**
目录下的容器里,你可以找到我们部署的用于 PostgreSQL 的元数据存储 URL。此外,还需要在配置中指定 **druid_extensions_loadList**
,包括 **druid-kafka-indexing-service**
在内。你还可以通过在环境配置中定义 **druid_indexer_runner_javaOptsArray**
来限制 Druid 的内存。
Apache Druid 组件在我的配置中。
1-) 动物园管理员
- 负责 Druid 进程之间的服务发现和协调。
- 担任集群管理的核心,并存储元数据,例如领导者选举的信息。
2. 协调人:
- 处理数据管理任务,如分段放置任务、负载均衡任务和删除任务。
- 确保数据在各个历史时间点间均匀分布。
3-) 中介
- 响应用户的查询。
- 作为网关,将查询转发到历史节点和实时节点之后,再合并结果。
- 历史 :
- 存储不可变的预处理数据(分段)。
- 响应代理请求的数据查询。
五) 中层经理
- 执行摄取任务,将新数据引入系统。
- 暂时存储数据,直到交给历史节点处理。
6) 路由器:
- 为所有 Druid API 提供单一入口。
- 将查询和 API 调用路由到适当的服务器(例如 broker 和 coordinator)。
然后我们需要将我们的 kafka brokers 与 apache druid 对接起来
你可以通过 **localhost:8081**
进入 Druid,然后依次点击 加载数据 > Apache Kafka > 连接数据。
你需要输入你的完整 Kafka IP 地址。这个功能非常有用,因为它能让你把多个主题整合到一个表格里。
你也可以在过滤部分筛选你的数据。虽然在这个项目中我没有使用到所有的功能,Apache Druid 还是提供了很多强大的功能。确保将 参数设置 > 启用早期偏移 设置为 False — 这可以确保我们接收到完整的数据集。虽然我们不一定需要所有数据,这样可以让我们在流式仪表板上看到更清晰的视图。你可以将其他设置保持为默认值。
第八部分:超集Superset 也是一个 Apache 工具,我们只是使用它来创建主题(topics)并设置一个 仪表盘。最初,我没有为创建 Superset 账户(account)编写一个 **.sh**
脚本,因为我希望手动展示账户创建的过程。
之后,你可以关闭程序并打开浏览器中的 Superset 面板,在 **localhost:8088**
。然后你需要将我们的 Apache Druid 表(**low**
、**mid**
、**high**
)导入 Superset。
首先,我们得连接到我们的Apache Druid 代理,因为Druid通过代理来访问源数据,并使用PostgreSQL来存储元数据。因此,我们需要使用Apache Druid 代理。
提示:别忘了,你需要首先在控制台创建用户,之后你就可以看如何创建仪表板的那部分了。你可以看文章最后的部分哦。
你需要挑出 Druid 这个选项。
连接数据库后,你可以在Superset中添加你的 **低等|中等|高等**
表。
德鲁伊(druid):低|中|高
如你所知,首先我们使用Apache Spark对数据进行分割并进行过滤,生成**low**
、**mid**
和**high**
类别。然后,我们将这些数据在Apache Druid中进行合并,并通过Superset读取这些数据。在主菜单==>图表==>中
创建图表时,你非常灵活,因为Superset提供了丰富的数据可视化功能。我搭建了一个基本的仪表板,将我们的 **低段**
、**中段**
和 **高段**
数据进行划分并计数。为此,我们需要使用自定义SQL查询。
CASE
WHEN 「balance < 500」 THEN «低»
WHEN 「balance BETWEEN 500 AND 2000」 THEN «中»
WHEN 「balance > 2000」 THEN «高»
END
这里有图表示例:
保存完仪表板后,最后一步就是设置实时流数据:
最后,你需要设置实时流数据面板的时间间隔:
就这样,我们就成功地把仪表板部署好了。
怎么启动项目的方法现在我们已经部署了容器,让我们回顾一下如何启动容器、配置容器并运行这个项目。
1-) 首先,确保你克隆了我的代码库。
git clone asdasdasd.git
这行命令用于克隆名为asdasdasd的git仓库。
接下来,进入项目文件夹下的容器文件夹。
docker-compose up -d --build
启动并构建所有服务 (Start and build all services)
下载过程中,请耐心等待。所有容器启动完毕后,请额外等待2-3分钟让配置完成。
- 首先,我们需要创建我们的 Kafka 主题(topic)。虽然你可以使用 Provectus Kafka-UI 来完成这一步,但我将演示如何在控制台中创建它。
docker exec -it kafka1 bash
./kafka-topics.sh --create --bootstrap-server localhost:9192 --topic raw --partitions 3 --replication-factor 3
./kafka-topics.sh --create --bootstrap-server localhost:9192 --topic low --partitions 3 --replication-factor 3
./kafka-topics.sh --create --bootstrap-server localhost:9192 --topic mid --partitions 3 --replication-factor 3
./kafka-topics.sh --create --bootstrap-server localhost:9192 --topic high --partitions 3 --replication-factor 3
如我在提到的卡夫卡部分,由于我们有3个节点,我们在server.properties
中设置了partitions=3
。如果 我们没有3个节点,我们就不能设置partitions=3
为3了。
当我们部署 kafka 集群时,我们需要创建 kafka 架构注册表。
首先,输入到kafka-schema-registry容器
运行bash shell进入kafka模式注册表容器
docker exec -it kafka-schema-registry bash
之后:
curl -X POST -H "Content-Type: application/vnd.schemaregistry.v1+json" \
--data '{
"schema": "{\"type\": \"record\", \"name\": \"Transaction\", \"fields\": [ {\"name\": \"id\", \"type\": \"string\"}, {\"name\": \"first_name\", \"type\": \"string\"}, {\"name\": \"last_name\", \"type\": \"string\"}, {\"name\": \"balance\", \"type\": \"double\"} ] }"
}' \
http://kafka-schema-registry:18081/subjects/raw-value/versions
这命令会发送一个包含交易模式的POST请求到Kafka模式注册表。
你可以在浏览器中查看你的数据模型。
http://localhost:18081/subjects/raw-value/versions/1
5-) 在那之后,启动 Scala Spark 的 JAR 文件,并确保你在 **containers/config/spark/submitfiles**
目录中。
你可能想知道为什么我在 **spark-submit**
这一行添加了这些包。与 Python 不同的是,Scala Spark 没有类似 **find-spark**
的工具,所以 **spark-submit**
有时可能找不到本地的 JAR 文件。这就是为什么我在命令中直接包含了这些包。
你需要把 jar 文件复制到文件夹里:
chmod +x scala-spark-submit.sh
将scala-spark-submit.sh文件的权限改为可执行
然后:
./scala-spark-submit.sh
注:./scala-spark-submit.sh
是一个用于提交Scala Spark任务的脚本。
Spark 会自动下载 JAR 文件。
6-) 启动生成器。如第一节所述,确保你已创建了数据集。你可以在此路径找到Python脚本:root_project_folder ==> datagenerator ==> json ==> producer
。
备注: 我在说 **datagenerator**
部分时创建了它,请看第四个帖子。
在命令行中输入以下命令来运行名为producer.py的生产者脚本:
python producer.py
7-) 一旦你开始生成和消费数据,你就可以访问Apache Druid(专有名词)并通过访问Apache Druid在localhost:8081
创建你的低
、中等
和高
表。我们之前讨论过如何在Apache Druid中创建这些表。
8-) 之后,您可以创建您的流媒体监控面板在 **localhost:8088**
。我们之前讲过如何设置您的流媒体监控面板。
进入 Superset 容器环境:
在superset容器中打开bash终端:docker exec -it superset bash
更新 Superset 数据库
superset db, 更新数据库
创建一个管理账户
superset fab create-admin --username cagri --firstname Superset --lastname Admin --email admin@superset.com --password 35413541
启动 Superset:
superset 启动
9-) 如果你需要重启项目,你可能需要清理日志文件。转到容器 > data_logs 并清理该文件夹,因为我已经将所有容器日志设置为使用 Docker 卷。在同一路径下,我提供了一个 **data_log_rm.sh**
脚本,可以用来清除日志文件。
运行数据日志删除脚本 ./data_log_rm.sh
**.sh**
脚本不会影响您的Kafka架构,因为重启容器不会删除您的Kafka主题。但是,如果您需要清除Kafka代理日志或数据,可以在此路径下运行以下命令:
执行命令 sudo rm -rf ./data_logs/kafka_data/*
来删除 kafka_data 目录下的所有文件。
我知道这个项目很庞大,包含许多配置步骤,但相信我,一旦你部署并开始使用,你将学到很多。如果你需要任何帮助,有任何问题都可以在LinkedIn上找我——我的LinkedIn个人档案链接在我的Medium简介里。
大家平平安安 :)
共同学习,写下你的评论
评论加载中...
作者其他优质文章