目录
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 特有参数配置
groupId = org.apache.spark
artifactId = spark-sql-kafka-0-10_2.11
version = 2.4.0
// 订阅一个主题
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)]
// 订阅一个主题, 默认使用最早、最新的偏移量
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)]
数据源中各字段模式:
Column | Type |
key | binary |
value | binary |
topic | string |
partition | int |
offset | long |
timestamp | long |
timestampType | int |
Kafka source必需的参数如下:
Option | value | meaning |
assign | JSON字符串:{"topicA":[0,1],"topicB":[2,4]} | 指定主题分区消费。只有“assgin”,"subscribe"或"subscribePattern"当中的一个可以指定给Kafka source, |
subscribe | 逗号分隔的主题列表 | 需要订阅的主题。只有“assgin”,"subscribe"或"subscribePattern"当中的一个可以指定给Kafka source, |
subscribePattern | Java正则字符串 | 用来订阅主题的正则表达式。只有“assgin”,"subscribe"或"subscribePattern"当中的一个可以指定给Kafka source, |
kafka.bootstrap.servers | 逗号分隔的主机端口列表 | Kafka的"bootstrap.servers"配置 |
下述配置是可选的:
Option | value | default | query type | meaning |
startingOffsets | "earliest", "latest" (streaming only), or json string """ {"topicA":{"0":23,"1":-1},"topicB":{"0":-2}} """ | "latest" for streaming, "earliest" for batch | streaming and batch | 查询的起点。“earliest”是从最早的offset起,“latest”最新的offset.也可使用json字符串来指定每个主题分区的起点和结束点。在json中 -2代表最早,-1代表最新。在批查询中latest不可用,流查询中,只有启动新查询时会使用这个值,并在下一次查询中重新获取(上一次查询的结束点)。新发现的分区会从最早点开始 |
endingOffsets | latest or json string {"topicA":{"0":23,"1":-1},"topicB":{"0":-1}} | latest | batch query | 批查询的结束点。... |
failOnDataLoss | true or false | true | streaming query | 当数据可能丢失时查询是否会失败。这可作为失败预警,当它不能按预期工作时可以禁用掉。批查询时,如果因为数据丢失而获取不到数据,则查询总是会失败 |
kafkaConsumer.pollTimeoutMs | long | 512 | streaming and batch | 检测executor中kafka数据的超时时间,单位毫秒 |
fetchOffset.numRetries | int | 3 | streaming and batch | 放弃获取kafka offset前尝试的次数 |
fetchOffset.retryIntervalMs | long | 10 | streaming and batch | 重试获取kfaka offset前的等待时间 |
maxOffsetsPerTrigger | long | none | streaming and batch | 每个trigger间隔处理的最大offset数的比率。指定的offset总数会被成比例的分割到不同容量的主题分区去。 |
这里我们讨论如何将流查询和批查询结果写出到Apache Kafka.需要注意的是Apache Kafka只支持最少一次的写算法。所以, 当写到Kafka时(不管是流查询还是批查询),有些消息可能会重复。这是可能发生的,例如,kafka需要重试没有被Broker确认的信息时,即使Broker已经接收到并且写入了这个消息。因为kafka的这个写算法,Structured Streaming 不能保证这样的重复不发生。不过,如果写操作成功,你可以认为这个查询结果最少被写出了一次。一个在读取这些数据时去除重的方案是引入一个唯一key。
写出到Kafka的DataFrame应该有下列结构:
Column | Type |
key(可选) | string或 binary |
value(必须) | string 或 binary |
topic(*option) | string |
* 如果主题的配置选项没有指定,那么topic这一列是必须的。
value 列是唯一一个必须的。如果key 列没有指定,会默认指定为null。如果主题列不存在,那么他的值会被用作主题写给kafka,除非设置了主题配置。主题配置会覆盖主题列。
Kafka sink 必须配置下列选项:
option | value | meaning |
kafka.bootstrap.servers | 逗号分隔的主机:端口列表 | Kafka "bootstrap.servers" 配置 |
下述参数是可选的:
Option | value | default | query type | meaning |
topic | string | none | streaming and batch | 设置写出的kafka 主题,这个配置将覆盖数据中的主题列配置 |
// 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()
/ 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()
Kafka特有的配置可以通过DataStreamReader.option的kafka.前缀配置。例如 stream.option("kafka.bootstrap.servers", "host:port"),kafka可用的参数可以参见kafka生产者/消费者参数配置文档。
请注意,下述参数不可以设置,否则kafka source 或sink将会抛出异常: