我有一个火花流媒体工作,从Kafka读取并与Postgres中的现有表格进行一些比较,然后再次写入Postrges。这就是它的样子:
val message = KafkaUtils.createStream(...).map(_._2)
message.foreachRDD( rdd => {
if (!rdd.isEmpty){
val kafkaDF = sqlContext.read.json(rdd)
println("First")
kafkaDF.foreachPartition(
i =>{
val jdbcDF = sqlContext.read.format("jdbc").options(
Map("url" -> "jdbc:postgresql://...",
"dbtable" -> "table", "user" -> "user", "password" -> "pwd" )).load()
createConnection()
i.foreach(
row =>{
println("Second")
connection.sendToTable()
}
)
closeConnection()
}
)
此代码在val jbdcDF = ...
行
我做错了什么?此外,我的日志"First"
有效,但"Second"
并未显示在日志中的任何位置。我用kafkaDF.collect().foreach(...)
尝试了整个代码并且它运行良好,但性能非常差。我希望用foreachPartition
替换它。
由于
答案 0 :(得分:3)
目前尚不清楚createConnection
,closeConnection
或connection.sendToTable
内是否存在任何问题,但基本问题是尝试嵌套操作/转换。它不支持Spark和Spark Streaming没有什么不同。
这意味着嵌套的DataFrame
初始化(val jdbcDF = sqlContext.read.format ...
)根本无法工作,应该被删除。如果您将其用作参考,则应在与kafkaDF
相同的级别创建,并使用标准转换(unionAll
,join
,...)进行参考。
如果由于某种原因它不是一个可接受的解决方案,你可以在forEachPartition
内创建普通的JDBC连接并在PostgreSQL表上运行(我想这是你在sendToTable
内已经做过的事情)。 / p>
答案 1 :(得分:1)
正如@ zero323正确指出的那样,你不能广播你的jdbc连接,也不能创建嵌套的RDD。 Spark根本不支持在现有闭包中使用sparkContext或sqlContext,即foreachPartition,因此空指针异常。
有效解决这个问题的唯一方法是在foreachPartition中创建JDBC连接并直接在其上执行SQL以执行任何操作,然后使用相同的连接写回记录。
关于你的第二个编辑问题:
变化:
kafkaDF.foreachPartition(..)
到
kafkaDF.repartition(numPartition).foreachPartition(..)
其中numPartition是所需的分区数。这将增加分区数量。如果您有多个执行程序(每个执行程序有多个任务),它们将并行运行。