使用 Apache Spark、Apache Kafka 和 ClickHouse 的实时数据处理架构

328 阅读6分钟

我们主要讨论使用 Apache Spark、Apache Kafka 和 ClickHouse 的实时数据处理架构。

什么是实时数据处理架构?

实时数据处理架构涉及实时/近实时处理数据。它在数据生成后立即处理数据并提供见解。

比方说,我们正在获取有关用户如何使用我们的 Web 应用程序的事件(数据)。为了简单起见,我们会收集用户单击内容、将鼠标悬停在内容上等时的事件。使用这些收集的事件,我们希望为单个用户提供定制的体验。为了提供定制体验,我们希望在收集的事件生成后立即对其进行处理。处理延迟会影响用户体验。理想情况下,每当用户与我们的网络应用程序交互时,我们都会立即处理收集到的数据并提供定制体验。

如果您不了解任何一个术语(Apache Spark | Apache Kafka | ClickHouse | Amazon S3),我建议您从下面的链接中学习该教程。

Apache Spark : Apache Spark 是一个分布式计算框架。为了简单起见,通俗地说,假设我们有一个非常大的数据集,我们想要对其进行一些操作(比如,我们想要计算特定单词的出现次数)。为了执行此操作,我们有一组机器。我们决定将数据集分割成子集,并将子集分布到机器上来执行我们的操作。我们这样认为,我们可以有效地利用可用的机器。但跨机器维护子集很复杂。如果有一个框架可以处理跨机器的数据集分割,并处理一致性和容错性,该怎么办?是的,Apache Spark 提供了这样一个框架。

Apache Kafka:Apache Kafka 是一个消息流组件。它提供了一个高吞吐量、低延迟的平台来传输数据。

Clickhouse: ClickHouse 是一个开源的面向列的 DBMS。使用基于 SQL 的查询生成分析报告非常有用。此外,ClickHouse 还配备了 Kafka 引擎,我们可以使用它从 Kafka 主题中读取/写入数据。

Amazon S3: Amazon S3 是一种对象存储服务,是 AWS 生态系统的一部分。它提供了高度可用且可扩展的存储服务。

先决条件:

要实现此架构,我们必须安装 Apache Spark、Apache Kafka 和 ClickHouse。

实时数据处理架构:

Image description

在此架构中,ClickHouse 被视为单一事实来源。其他服务/组件将数据存储到 ClickHouse 中。

  1. 每当应用程序向 ClickHouse 表中插入一条记录时,插入的记录将由 ClickHouse Kafka Engine 推送到 Kafka 中。(请参阅架构图中的步骤 A)。
  2. Apache Spark 使用 Kafka-Spark 连接器从 Kafka 传输实时数据。利用这个数据流,Spark可以进行实时处理。(参考步骤B)
  3. 处理完成后,会将结果存储到S3存储桶中。(参考步骤C)

该架构的实现涉及五个步骤:

  • Kafka-Apache Spark 连接器设置。
  • Apache Spark-Amazon S3 连接器设置。
  • 配置 ClickHouse-Kafka。
  • 收听 Apache Spark 中的 Kafka 主题。
  • 将结果存储到 Amazon S3 中。
Kafka-Apache Spark 连接器设置:

要监听 Kafka 主题,我们需要以下 jar。这些 jar 将为 Apache Spark 提供监听 Kafka 主题的接口。

此外,Apache Spark 能够监听多个 Kafka 主题。

  • Spark-sql-kafka-0–10_2.12–3.1.2.jar
  • Spark-token-provider-kafka-0–10_2.12–3.1.2.jar
  • kafka-clients-3.0.0.jar
  • commons-pool2–2.11.1.jar

将这些 jar 复制到 $SPARK_HOME/jars/ 文件夹中。

wget https://repo1.maven.org/maven2/org/apache/spark/spark-sql-kafka-0-10_2.12/3.1.2/spark-sql-kafka-0-10_2.12-3.1.2 .jar -o $SPARK_HOME/jars/
wget https://repo1.maven.org/maven2/org/apache/spark/spark-token-provider-kafka-0-10_2.12/3.1.2/spark-token-provider-kafka-0-10_2.12 -3.1.2.jar -o $SPARK_HOME/jars/
wget https://repo1.maven.org/maven2/org/apache/kafka/kafka-clients/3.0.0/kafka-clients-3.0.0.jar -o $SPARK_HOME/jars/
wget https://repo1.maven.org/maven2/org/apache/commons/commons-pool2/2.11.1/commons-pool2-2.11.1.jar -o $SPARK_HOME/jars/

提示:默认情况下,Apache Spark 作业从 $SPARK_HOME/jars/ 文件夹加载 jar。您不必将 jar 复制到此文件夹中。相反,您可以在使用 —conf 参数提交 Spark 作业时加载这些 jar。请参阅此处以了解更多相关信息。

Apache Spark-Amazon S3 Bucket Connector:

要将数据写入 Amazon S3 存储桶,我们需要以下 jar。这些 jar 提供了一个接口,用于将数据从 Apache Spark 写入 Amazon S3 存储桶。

  • aws -java-sdk-core-1.11.762.jar
  • aws -java-sdk-1.11.762.jar
  • aws -java-sdk-dynamodb-1.11.762.jar
  • aws -java-sdk-s3–1.11.762.jar
  • hadoop -aws-3.1.2.jar

将这些 jar 复制到 $SPARK_HOME/jars/ 文件夹中。

wget https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk-core/1.11.762/aws-java-sdk-core-1.11.762.jar -o $SPARK_HOME/jars/
wget https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk/1.11.762/aws-java-sdk-1.11.762.jar -o $SPARK_HOME/jars/
wget https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk-dynamodb/1.11.762/aws-java-sdk-dynamodb-1.11.762.jar -o $SPARK_HOME/jars/
wget https://repo1.maven.org/maven2/com/amazonaws/aws-java-sdk-s3/1.11.762/aws-java-sdk-s3-1.11.762.jar -o $SPARK_HOME/jars/
wget https://repo1.maven.org/maven2/org/apache/hadoop/hadoop-aws/3.1.2/hadoop-aws-3.1.2.jar -o $SPARK_HOME/jars/

提示:默认情况下,Apache Spark 作业从 $SPARK_HOME/jars/ 文件夹加载 jar。您不必将 jar 复制到此文件夹中。相反,您可以在使用 —conf 参数提交 Spark 作业时加载这些 jar。请参阅此处以了解更多相关信息。

配置 ClickHouse — Kafka:

借助 ClickHouse — Kafka 引擎,我们可以将 ClickHouse 表中的记录推送到 Kafka,反之亦然。

在我们的演示中,比如说,我们的要求是,每当我们在API表中插入SERVICE_ID = 1 的记录时,插入的记录都必须推送到 Kafka 中。

  • ClickHouse 源表— 创建源表API。每当我们向该表中插入一条记录时,ClickHouse Kafka 引擎就会将数据推送到 Kafka。
CREATE TABLE API ( 
        api_id Int32 Codec(DoubleDelta, LZ4), 
        time DateTime Codec(DoubleDelta, LZ4), 
        time_taken DateTime Codec(DoubleDelta, LZ4), 
        Service_id Int32 
) Engine = MergeTree 
PARTITION BY toYYYYMM(time) 
ORDER BY ( api_id , time);
  • Kafka 主题 ( kafka_topic_1 ) — 创建一个目标 Kafka 主题,其中 Kafka 引擎推送满足条件 SERVICE_ID = 1 的 ClickHouse 记录。
kafka-topics \ 
--bootstrap-server kafka:9092 \ 
--topic kafka_topic_1 \ 
--create --partitions 6 \ 
--replication-factor 2
  • 使用创建的Kafka主题对应的Kafka表引擎创建ClickHouse表(kafka_queue) 。
CREATE TABLE kafka_queue ( 
    api_id Int32, 
    time_taken Int32 
) 
ENGINE = Kafka 
SETTINGS kafka_broker_list = 'kafka:9092 ', 
       kafka_topic_list = 'kafka_topic_1 ', 
       kafka_format = 'CSV', 
       kafka_max_block_size = 1 048576;
  • 创建一个物化视图 ( kafka_queue_mv) — 它将 service_id = 1 的行从API表传输到kafka_queue表。
CREATE MATERIALIZED VIEW kafka_queue_mv TO kafka_queue_mv AS 
SELECT api_id, time_taken FROM API
 WHERE service_id = 1

最后,我们需要运行一个Kafka消费者来测试。

kafka-console-consumer.sh --bootstrap-server kafka:9092 -- topic kafka_topic_1

每当您向 service_id = 1 的 API 表插入一条记录时,该记录就会被推送到 kafka_topic_1 。

从 Apache Spark 监听 Kafka 主题:

要从 Apache Spark 作业读取 Kafka 流,我们需要以下信息。

  • Kafka 服务器域名/IP 地址。
  • Kafka 服务器运行的端口。
  • 要听的 Kafka 主题。

Apache Spark 能够从多个 Kafka 实例监听多个 Kafka 主题。为了演示,下面的代码片段从 kafka:9092 实例监听主题 kafka-topic-1

dataframe = Spark 
       .readStream 
       .format("kafka") 
       .option("kafka.bootstrap.servers", "kafka:9092") 
       .option("consumer", "kafka-topic-1") 
       .load()

Image description

加载后,下一步是计算 API 所花费的时间百分比。

dataframe 
   .withColumn(“percentage_time”,percent_rank().over(windowSpec)) 
   .show()

Image description

下一步是,我们需要将流写入 Amazon S3 存储桶。

注意: Apache Spark 能够将流写入许多数据层,包括 HDFS、Cassandra 等。

将结果存储到 Amazon S3 存储桶中

要将 Apache Spark 中的数据存储到 Amazon S3 存储桶,我们需要 S3 存储桶访问 ID 和密钥。Apache Spark 使用此访问 ID 和密钥向 AWS 进行身份验证。

我们有两个选项来设置 Access Id 和 Secret Key:

将访问 ID 和密钥存储为环境变量 — 不是首选方式。原因是,我们提交的任何 Spark 作业都可以访问 access_id 和 Secret_key,因为它被设置为环境变量。

export AWS_SECRET_ACCESS_KEY=XXXXX
export AWS_ACCESS_KEY_ID=XXXXX

在 SparkContext 中设置 Access Id 和 Secret Key — 另一种选择是,我们可以在 Spark Context 对象中设置 Access Id 和 Secret Key。

Spark.sparkContext._jsc.hadoopConfiguration() 
        .set(“fs.s3a.access.key”, xxxxxxx )
Spark.sparkContext._jsc.hadoopConfiguration() 
        .set(“fs.s3a.secret.key”, xxxxxxxxxxxxxxxxxxx )

在上面的代码片段中,我们设置 fs.s3a.access.keyfs.s3a.secret.key 参数。

一旦我们设置了访问密钥和秘密密钥,下一步就是编写将数据实际存储到 S3 存储桶中的代码。将 Spark 数据帧写入 S3 存储桶类似于写入 HDFS 或本地文件系统。