Scala Spark Structured Streaming with Kafka-如何重新划分数据并在工作节点之间分配处理
如果我的卡夫卡主题收到如下记录Scala Spark Structured Streaming with Kafka-如何重新划分数据并在工作节点之间分配处理,scala,apache-spark,apache-kafka,spark-structured-streaming,spark-kafka-integration,Scala,Apache Spark,Apache Kafka,Spark Structured Streaming,Spark Kafka Integration,如果我的卡夫卡主题收到如下记录 CHANNEL | VIEWERS | ..... ABC | 100 | ..... CBS | 200 | ..... 我使用Spark结构化流式代码读取和处理卡夫卡记录,如下所示: val spark = SparkSession .builder .appName("TestPartition") .master("local[*]") .getOrCreate()
CHANNEL | VIEWERS | .....
ABC | 100 | .....
CBS | 200 | .....
我使用Spark结构化流式代码读取和处理卡夫卡记录,如下所示:
val spark = SparkSession
.builder
.appName("TestPartition")
.master("local[*]")
.getOrCreate()
import spark.implicits._
val dataFrame = spark
.readStream
.format("kafka")
.option("kafka.bootstrap.servers",
"1.2.3.184:9092,1.2.3.185:9092,1.2.3.186:9092")
.option("subscribe", "partition_test")
.option("failOnDataLoss", "false")
.load()
.selectExpr("CAST(value AS STRING)")
// I will use a custom UDF to transform to a specific object
val writer = new ForeachWriter[testRec] {
def open(partitionId: Long, version: Long): Boolean = {
true
}
def process(record: testRec) = {
handle(record)
}
def close(errorOrNull: Throwable): Unit = {
}
}
val query = dataFrame.writeStream
.format("console")
.foreach(writer)
.outputMode("append")
.start()
目前,我使用foreachwriter处理记录,如下所示:
val spark = SparkSession
.builder
.appName("TestPartition")
.master("local[*]")
.getOrCreate()
import spark.implicits._
val dataFrame = spark
.readStream
.format("kafka")
.option("kafka.bootstrap.servers",
"1.2.3.184:9092,1.2.3.185:9092,1.2.3.186:9092")
.option("subscribe", "partition_test")
.option("failOnDataLoss", "false")
.load()
.selectExpr("CAST(value AS STRING)")
// I will use a custom UDF to transform to a specific object
val writer = new ForeachWriter[testRec] {
def open(partitionId: Long, version: Long): Boolean = {
true
}
def process(record: testRec) = {
handle(record)
}
def close(errorOrNull: Throwable): Unit = {
}
}
val query = dataFrame.writeStream
.format("console")
.foreach(writer)
.outputMode("append")
.start()
代码运行得很好。但是,我想做的是将传入的数据按通道进行分区,这样每个工人都负责特定的通道,我在handle()块中进行与该通道相关的内存计算。可能吗?如果是,我如何做到这一点?代码按原样在记录级别应用
handle
方法,并且与记录的分区无关
我看到两个选项可确保同一通道的所有消息将在同一执行器上处理:
channel
的值设置为Kafka消息的键。默认情况下,KafkaProducer使用键定义数据写入的分区。这将确保具有相同密钥的所有消息都将到达相同的Kafka主题分区。由于使用卡夫卡主题的Spark结构化流媒体作业将匹配卡夫卡分区,因此生成的dataFrame
将具有与卡夫卡主题相同的分区数量,并且同一频道的所有消息都位于同一分区中
dataFrame.repartition(n,col(“columnName”)
,根据列channel
的值简单地重新划分dataFrame
,其中n
是分区数。这样,具有相同通道的所有记录都将落在同一分区中,因此将在同一执行器上处理
- 拥有分区(数据帧或Kafka主题)的所有权需要额外注意,因为最终可能会出现所谓的“数据倾斜”。与只有少量消息的分区相比,当您有包含大量消息的分区时,就会发生数据倾斜。这将对您的整体绩效产生负面影响
- 只要您使用的是
输出接收器,在记录级别处理数据时,数据的分区方式就无关紧要了。如果您正在寻找更多的控件,您可以使用foreach
sink(Spark 2.4+中提供)。foreachBatch输出接收器使您可以控制每个微批次的批次数据帧,并且您可以使用foreachBatch
或foreachPartitions
执行基于分区的逻辑mapPartitions