Structured Streaming集成Kafka《官方文档翻译》

本文详细介绍了如何使用Apache Spark从Kafka读取数据并写入Kafka,包括流查询和批查询的方法,以及Kafka特有参数配置,是Spark与Kafka集成的实用参考。

目录

1. 链接

2. 从Kafka读数据

2.1 从流查询创建Kafka数据源

2.2 从批查询Kafka数据源(spark.readStream变成了spark.read)

3. 向Kafka写数据

3.1 创建流查询Kafka Sink

3.2 创建批查询Kafka Sink

4 Kafka 特有参数配置


1. 链接

groupId = org.apache.spark
artifactId = spark-sql-kafka-0-10_2.11
version = 2.4.0

2. 从Kafka读数据

2.1 从流查询创建Kafka数据源

// 订阅一个主题
val df = spark
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "idayan00:9092,idayan01:9092,idayan02:9092")
  .option("subscribe", "topicA")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

//  订阅多个主题
val df = spark
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "idayan00:9092,idayan01:9092,idayan02:9092")
  .option("subscribe", "topicA,topicA")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

// 订阅通过正则匹配的主题
val df = spark
  .readStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "idayan00:9092,idayan01:9092,idayan02:9092")
  .option("subscribePattern", "topic*")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

2.2 从批查询Kafka数据源(spark.readStream变成了spark.read)

// 订阅一个主题, 默认使用最早、最新的偏移量
val df = spark
  .read
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:9092,host2:9092")
  .option("subscribe", "topicA")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

// 订阅多个主题,, 指定明确的 Kafka 偏移量
val df = spark
  .read
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:9092,host2:9092")
  .option("subscribe", "topicA,topicA")
  .option("startingOffsets", """{"topic1":{"0":23,"1":-2},"topic2":{"0":-2}}""")
  .option("endingOffsets", """{"topic1":{"0":50,"1":-1},"topic2":{"0":-1}}""")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

// 订阅通过正则匹配的主题, 使用最早、最新的偏移量
val df = spark
  .read
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("subscribePattern", "topic.*")
  .option("startingOffsets", "earliest")
  .option("endingOffsets", "latest")
  .load()
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .as[(String, String)]

数据源中各字段模式:

ColumnType
keybinary
valuebinary
topicstring
partitionint
offsetlong
timestamplong
timestampTypeint

Kafka source必需的参数如下:

Optionvaluemeaning
assignJSON字符串:{"topicA":[0,1],"topicB":[2,4]}指定主题分区消费。只有“assgin”,"subscribe"或"subscribePattern"当中的一个可以指定给Kafka source,
subscribe逗号分隔的主题列表需要订阅的主题。只有“assgin”,"subscribe"或"subscribePattern"当中的一个可以指定给Kafka source,
subscribePatternJava正则字符串用来订阅主题的正则表达式。只有“assgin”,"subscribe"或"subscribePattern"当中的一个可以指定给Kafka source,
kafka.bootstrap.servers逗号分隔的主机端口列表Kafka的"bootstrap.servers"配置

下述配置是可选的:

Optionvaluedefaultquery typemeaning
startingOffsets"earliest", "latest" (streaming only), or json string """ {"topicA":{"0":23,"1":-1},"topicB":{"0":-2}} """ "latest" for streaming, "earliest" for batchstreaming and batch查询的起点。“earliest”是从最早的offset起,“latest”最新的offset.也可使用json字符串来指定每个主题分区的起点和结束点。在json中 -2代表最早,-1代表最新。在批查询中latest不可用,流查询中,只有启动新查询时会使用这个值,并在下一次查询中重新获取(上一次查询的结束点)。新发现的分区会从最早点开始
endingOffsetslatest or json string {"topicA":{"0":23,"1":-1},"topicB":{"0":-1}} latestbatch query批查询的结束点。...
failOnDataLosstrue or falsetruestreaming query当数据可能丢失时查询是否会失败。这可作为失败预警,当它不能按预期工作时可以禁用掉。批查询时,如果因为数据丢失而获取不到数据,则查询总是会失败
kafkaConsumer.pollTimeoutMslong512streaming and batch检测executor中kafka数据的超时时间,单位毫秒
fetchOffset.numRetriesint3streaming and batch放弃获取kafka offset前尝试的次数
fetchOffset.retryIntervalMslong10streaming and batch重试获取kfaka offset前的等待时间
maxOffsetsPerTriggerlongnonestreaming and batch每个trigger间隔处理的最大offset数的比率。指定的offset总数会被成比例的分割到不同容量的主题分区去。

3. 向Kafka写数据

这里我们讨论如何将流查询和批查询结果写出到Apache Kafka.需要注意的是Apache Kafka只支持最少一次的写算法。所以, 当写到Kafka时(不管是流查询还是批查询),有些消息可能会重复。这是可能发生的,例如,kafka需要重试没有被Broker确认的信息时,即使Broker已经接收到并且写入了这个消息。因为kafka的这个写算法,Structured Streaming 不能保证这样的重复不发生。不过,如果写操作成功,你可以认为这个查询结果最少被写出了一次。一个在读取这些数据时去除重的方案是引入一个唯一key。

    写出到Kafka的DataFrame应该有下列结构:

ColumnType
key(可选)string或 binary
value(必须)string 或 binary
topic(*option)string

* 如果主题的配置选项没有指定,那么topic这一列是必须的。

    value 列是唯一一个必须的。如果key 列没有指定,会默认指定为null。如果主题列不存在,那么他的值会被用作主题写给kafka,除非设置了主题配置。主题配置会覆盖主题列。

Kafka sink 必须配置下列选项:

optionvaluemeaning
kafka.bootstrap.servers逗号分隔的主机:端口列表Kafka "bootstrap.servers" 配置

下述参数是可选的:

Optionvaluedefaultquery typemeaning
topicstringnonestreaming and batch设置写出的kafka 主题,这个配置将覆盖数据中的主题列配置

3.1 创建流查询Kafka Sink

// Write key-value data from a DataFrame to a specific Kafka topic specified in an option
val ds = df
  .selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .writeStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("topic", "topic1")
  .start()

// Write key-value data from a DataFrame to Kafka using a topic specified in the data
val ds = df
  .selectExpr("topic", "CAST(key AS STRING)", "CAST(value AS STRING)")
  .writeStream
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .start()

3.2 创建批查询Kafka Sink

/ Write key-value data from a DataFrame to a specific Kafka topic specified in an option
df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)")
  .write
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .option("topic", "topic1")
  .save()

// Write key-value data from a DataFrame to Kafka using a topic specified in the data
df.selectExpr("topic", "CAST(key AS STRING)", "CAST(value AS STRING)")
  .write
  .format("kafka")
  .option("kafka.bootstrap.servers", "host1:port1,host2:port2")
  .save()

4 Kafka 特有参数配置

Kafka特有的配置可以通过DataStreamReader.option的kafka.前缀配置。例如 stream.option("kafka.bootstrap.servers", "host:port"),kafka可用的参数可以参见kafka生产者/消费者参数配置文档。
    请注意,下述参数不可以设置,否则kafka source 或sink将会抛出异常:

  • group.id: Kafka Source会为每个查询创建一个唯一的group id.
  • auto.offset.reset: 设为source选项 startingOffsets为指定offset.
  • key.deserializer: key总是使用ByteArrayDeserializer反序列化为字节数组。
  • value.deserializer:value总是使用ByteArrayDeserializer反序列化为字节数组。
  • key.serializer: key总是使用ByteArraySerializer 或StringSerializer序列化。
  • value.serializer:value 总是使用ByteArraySerializer 或StringSerializer序列化。
  • enable.auto.commit: Kafka source 不提交任何offset
  • interceptor.classes: kafka source将values读取做字节数组。使用ConsumerInterceptor可能会破坏查询,因此是不安全的。
### 解决 Structured StreamingKafka 数据源未找到错误的部署指南 当遇到 `org.apache.spark.sql.AnalysisException: Failed to find data source: kafka` 错误时,通常是因为缺少必要的依赖项或配置不正确。以下是详细的解决方案: #### 1. 添加 Spark-Kafka 连接器依赖 为了使 Apache Spark 能够识别 Kafka 数据源,需要显式地引入 Spark 的 Kafka 连接器库。可以通过 Maven 或手动下载 JAR 文件来完成此操作。 如果使用的是 Maven 构建工具,则可以在项目的 `pom.xml` 中添加以下依赖项: ```xml <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql-kafka-0-10_2.12</artifactId> <version>{SPARK_VERSION}</version> </dependency> ``` 其中 `{SPARK_VERSION}` 应替换为实际使用的 Spark 版本号[^4]。 对于非 Maven 项目,可以访问 [Maven Repository](https://mvnrepository.com/artifact/org.apache.spark/spark-sql-kafka-0-10) 下载对应版本的 JAR 文件并将其放置到类路径下。 #### 2. 正确加载外部依赖 即使已将所需的 JAR 文件放入类路径中,在提交 Spark 作业时仍需通过参数指定这些文件的位置。例如,运行 Spark 提交命令时应附加如下选项: ```bash --packages org.apache.spark:spark-sql-kafka-0-10_2.12:{SPARK_VERSION} ``` 或者直接上传本地 JAR 到集群节点并通过 `--jars` 参数传递其位置: ```bash --jars /path/to/spark-sql-kafka-0-10_2.12-{SPARK_VERSION}.jar ``` #### 3. 验证环境配置 确保开发环境中安装了兼容版本的 Scala、Java JDK 及其他必要组件。此外还需确认网络连通性和权限设置允许应用程序连接至目标 Kafka 实例。 #### 4. 测试代码样例 下面提供了一个简单的测试程序用于验证集成是否成功: ```scala import org.apache.spark.sql.SparkSession val spark = SparkSession.builder() .appName("KafkaIntegrationTest") .getOrCreate() // 替换为您的 Kafka 主机地址和主题名称 val df = spark.readStream.format("kafka").option("kafka.bootstrap.servers", "localhost:9092").option("subscribe", "test-topic").load() df.selectExpr("CAST(key AS STRING)", "CAST(value AS STRING)").writeStream.outputMode("append").format("console").start().awaitTermination() ``` 以上脚本会尝试从名为 `test-topic` 的 Kafka Topic 中读取消息并将它们打印出来。如果一切正常工作则表明问题已被妥善处理[^1]。 #### 故障排除提示 尽管遵循上述指导方针应该能够解决问题,但在某些情况下可能还需要进一步排查原因。比如检查是否有防火墙阻止端口通信或是服务器端是否存在资源不足等情况影响服务启动等等[^3]。 #### 关于容错机制简介 值得一提的是,Kafka Connect 使用分区分配策略把数据分发给消费者组内的各个成员,从而实现负载均衡的同时也保障了即便某个节点发生故障也不会丢失任何记录,并维持较高的可用性水平以及一致性标准[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值