我正在将DStream
保存到Cassandra。卡桑德拉(Cassandra)中有一列,数据类型为map<text, text>
。 Cassandra在Map中不支持null
值,但流中可能出现空值。
如果情况出现问题,我已经添加了try
catch
,但是尽管如此,程序仍然停止了,并且我在日志中没有看到错误消息:
try {
cassandraStream.saveToCassandra("table", "keyspace")
} catch {
case e: Exception => log.error("Error in saving data in Cassandra" + e.getMessage, e)
}
例外
Caused by: java.lang.NullPointerException: Map values cannot be null
at com.datastax.driver.core.TypeCodec$AbstractMapCodec.serialize(TypeCodec.java:2026)
at com.datastax.driver.core.TypeCodec$AbstractMapCodec.serialize(TypeCodec.java:1909)
at com.datastax.driver.core.AbstractData.set(AbstractData.java:530)
at com.datastax.driver.core.AbstractData.set(AbstractData.java:536)
at com.datastax.driver.core.BoundStatement.set(BoundStatement.java:870)
at com.datastax.spark.connector.writer.BoundStatementBuilder.com$datastax$spark$connector$writer$BoundStatementBuilder$$bindColumnUnset(BoundStatementBuilder.scala:73)
at com.datastax.spark.connector.writer.BoundStatementBuilder$$anonfun$6.apply(BoundStatementBuilder.scala:84)
at com.datastax.spark.connector.writer.BoundStatementBuilder$$anonfun$6.apply(BoundStatementBuilder.scala:84)
at com.datastax.spark.connector.writer.BoundStatementBuilder$$anonfun$bind$1.apply$mcVI$sp(BoundStatementBuilder.scala:106)
at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:160)
at com.datastax.spark.connector.writer.BoundStatementBuilder.bind(BoundStatementBuilder.scala:101)
at com.datastax.spark.connector.writer.GroupingBatchBuilder.next(GroupingBatchBuilder.scala:106)
at com.datastax.spark.connector.writer.GroupingBatchBuilder.next(GroupingBatchBuilder.scala:31)
at scala.collection.Iterator$class.foreach(Iterator.scala:893)
at com.datastax.spark.connector.writer.GroupingBatchBuilder.foreach(GroupingBatchBuilder.scala:31)
at com.datastax.spark.connector.writer.TableWriter$$anonfun$writeInternal$1.apply(TableWriter.scala:233)
at com.datastax.spark.connector.writer.TableWriter$$anonfun$writeInternal$1.apply(TableWriter.scala:210)
at com.datastax.spark.connector.cql.CassandraConnector$$anonfun$withSessionDo$1.apply(CassandraConnector.scala:112)
at com.datastax.spark.connector.cql.CassandraConnector$$anonfun$withSessionDo$1.apply(CassandraConnector.scala:111)
at com.datastax.spark.connector.cql.CassandraConnector.closeResourceAfterUse(CassandraConnector.scala:145)
at com.datastax.spark.connector.cql.CassandraConnector.withSessionDo(CassandraConnector.scala:111)
at com.datastax.spark.connector.writer.TableWriter.writeInternal(TableWriter.scala:210)
at com.datastax.spark.connector.writer.TableWriter.insert(TableWriter.scala:197)
at com.datastax.spark.connector.writer.TableWriter.write(TableWriter.scala:183)
at com.datastax.spark.connector.streaming.DStreamFunctions$$anonfun$saveToCassandra$1$$anonfun$apply$1.apply(DStreamFunctions.scala:54)
at com.datastax.spark.connector.streaming.DStreamFunctions$$anonfun$saveToCassandra$1$$anonfun$apply$1.apply(DStreamFunctions.scala:54)
at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:87)
at org.apache.spark.scheduler.Task.run(Task.scala:109)
at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:345)
... 3 more
尽管有try / catch块,我想知道为什么程序会停止。为什么未捕获到异常?
答案 0 :(得分:1)
要了解失败的根源,您必须承认,DStreamFunctions.saveToCassandra
与总体上DStream
的输出操作相同,从严格意义上讲并不是一个动作。在实践中it just invokes foreachRDD
:
dstream.foreachRDD(rdd => rdd.sparkContext.runJob(rdd, writer.write _))
向此DStream中的每个RDD应用一个函数。这是一个输出运算符,因此“ this” DStream将被注册为输出流,并因此实现。
差异是细微的,但很重要-已注册了操作,但实际执行是在不同的上下文中进行的。
这意味着在您调用saveToCassandra
时不会出现运行时失败。
正如已经指出的,try
或Try
如果直接应用于动作,则将包含驱动程序异常。因此,例如,您将saveToCassandra
重新实现为
dstream.foreachRDD(rdd => try {
rdd.sparkContext.runJob(rdd, writer.write _)
} catch {
case e: Exception => log.error("Error in saving data in Cassandra" + e. getMessage, e)
})
尽管当前批次将全部或部分丢失,但流应该能够继续进行。
重要的是要注意,这与捕获原始异常不同,原始异常将在日志中被抛出,未捕获和可见。要从源头上发现问题,您必须直接在writer中应用try
/ catch
块,并且当您执行无法控制的代码时,这显然不是一个选择。 / p>
带走消息是(已在此线程中声明)-请确保清理数据以避免已知的故障源。
答案 1 :(得分:-1)
问题是您没有捕获到您认为的异常。您拥有的代码将捕获驱动程序异常,实际上,像这样构造的代码将做到这一点。
但这并不意味着
程序永远都不会停止。
当驱动程序故障(可能由致命的执行程序故障导致)被包含在内且驱动程序可以正常退出时,流已经消失了。因此,您的代码将退出,因为没有更多的流可以运行。
如果有问题的代码在您的控制之下,则应将异常处理委托给任务,但是对于第3方代码,则没有此类选项。
相反,您应先验证数据并删除有问题的记录,然后再将这些记录传递给saveToCassandra
。