web-dev-qa-db-ja.com

Spark Structured Streaming [SparkException:Task not serializable]]のJDBCシンクを記述する方法?

spark構造化ストリーミングデータフレーム用のJDBCシンクが必要です。現時点では、DataFrameのAPIにJDBC実装へのwriteStreamが不足しています(PySparkでも= Scala(current Spark version 2.2.0))私が見つけた唯一の提案は、自分のForeachWriter Scala この記事 に基づくクラス。

そこで、カスタムのForeachWriterclassを追加して here から単純なWordカウントの例を変更し、PostgreSQLにwriteStreamを追加しようとしました。単語のストリームはコンソールから手動で生成され(NetCat:nc -lk -p 9999を使用)、Spark=ソケットから読み取られます)。

残念ながら、「タスクがシリアル化できません」というエラーが表示されます。

Apache_SPARK_VERSION = 2.1.0 Scalaバージョン2.11.8(Java HotSpot(TM)64ビットサーバーVM、Java 1.8.0_112)を使用

私のScalaコード:

//Spark context available as 'sc' (master = local[*], app id = local-1501242382770).
//Spark session available as 'spark'.

import Java.sql._
import org.Apache.spark.sql.functions._
import org.Apache.spark.sql.SparkSession

val spark = SparkSession
  .builder
  .master("local[*]")
  .appName("StructuredNetworkWordCountToJDBC")
  .config("spark.jars", "/tmp/data/postgresql-42.1.1.jar")
  .getOrCreate()

import spark.implicits._

val lines = spark.readStream
  .format("socket")
  .option("Host", "localhost")
  .option("port", 9999)
  .load()

val words = lines.as[String].flatMap(_.split(" "))

val wordCounts = words.groupBy("value").count()

class JDBCSink(url: String, user:String, pwd:String) extends org.Apache.spark.sql.ForeachWriter[org.Apache.spark.sql.Row]{
    val driver = "org.postgresql.Driver"
    var connection:Java.sql.Connection = _
    var statement:Java.sql.Statement = _

    def open(partitionId: Long, version: Long):Boolean = {
        Class.forName(driver)
        connection = Java.sql.DriverManager.getConnection(url, user, pwd)
        statement = connection.createStatement
        true
    }

    def process(value: org.Apache.spark.sql.Row): Unit = {        
    statement.executeUpdate("INSERT INTO public.test(col1, col2) " +
                             "VALUES ('" + value(0) + "'," + value(1) + ");")
    }

    def close(errorOrNull:Throwable):Unit = {
        connection.close
    }
}

val url="jdbc:postgresql://<mypostgreserver>:<port>/<mydb>"
val user="<user name>"
val pwd="<pass>"
val writer = new JDBCSink(url, user, pwd)

import org.Apache.spark.sql.streaming.ProcessingTime

val query=wordCounts
  .writeStream
  .foreach(writer)
  .outputMode("complete")
  .trigger(ProcessingTime("25 seconds"))
  .start()

query.awaitTermination()

エラーメッセージ:

ERROR StreamExecution: Query [id = ef2e7a4c-0d64-4cad-ad4f-91d349f8575b, runId = a86902e6-d168-49d1-b7e7-084ce503ea68] terminated with error
org.Apache.spark.SparkException: Task not serializable
        at org.Apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:298)
        at org.Apache.spark.util.ClosureCleaner$.org$Apache$spark$util$ClosureCleaner$$clean(ClosureCleaner.scala:288)
        at org.Apache.spark.util.ClosureCleaner$.clean(ClosureCleaner.scala:108)
        at org.Apache.spark.SparkContext.clean(SparkContext.scala:2094)
        at org.Apache.spark.rdd.RDD$$anonfun$foreachPartition$1.apply(RDD.scala:924)
        at org.Apache.spark.rdd.RDD$$anonfun$foreachPartition$1.apply(RDD.scala:923)
        at org.Apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:151)
        at org.Apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:112)
        at org.Apache.spark.rdd.RDD.withScope(RDD.scala:362)
        at org.Apache.spark.rdd.RDD.foreachPartition(RDD.scala:923)
        at org.Apache.spark.sql.execution.streaming.ForeachSink.addBatch(ForeachSink.scala:49)
        at org.Apache.spark.sql.execution.streaming.StreamExecution$$anonfun$org$Apache$spark$sql$execution$streaming$StreamExecution$$runBatch$1.apply$mcV$sp(StreamExecution.scala:503)
        at org.Apache.spark.sql.execution.streaming.StreamExecution$$anonfun$org$Apache$spark$sql$execution$streaming$StreamExecution$$runBatch$1.apply(StreamExecution.scala:503)
        at org.Apache.spark.sql.execution.streaming.StreamExecution$$anonfun$org$Apache$spark$sql$execution$streaming$StreamExecution$$runBatch$1.apply(StreamExecution.scala:503)
        at org.Apache.spark.sql.execution.streaming.ProgressReporter$class.reportTimeTaken(ProgressReporter.scala:262)
        at org.Apache.spark.sql.execution.streaming.StreamExecution.reportTimeTaken(StreamExecution.scala:46)
        at org.Apache.spark.sql.execution.streaming.StreamExecution.org$Apache$spark$sql$execution$streaming$StreamExecution$$runBatch(StreamExecution.scala:502)
        at org.Apache.spark.sql.execution.streaming.StreamExecution$$anonfun$org$Apache$spark$sql$execution$streaming$StreamExecution$$runBatches$1$$anonfun$1.apply$mcV$sp(StreamExecution.scala:255)
        at org.Apache.spark.sql.execution.streaming.StreamExecution$$anonfun$org$Apache$spark$sql$execution$streaming$StreamExecution$$runBatches$1$$anonfun$1.apply(StreamExecution.scala:244)
        at org.Apache.spark.sql.execution.streaming.StreamExecution$$anonfun$org$Apache$spark$sql$execution$streaming$StreamExecution$$runBatches$1$$anonfun$1.apply(StreamExecution.scala:244)
        at org.Apache.spark.sql.execution.streaming.ProgressReporter$class.reportTimeTaken(ProgressReporter.scala:262)
        at org.Apache.spark.sql.execution.streaming.StreamExecution.reportTimeTaken(StreamExecution.scala:46)
        at org.Apache.spark.sql.execution.streaming.StreamExecution$$anonfun$org$Apache$spark$sql$execution$streaming$StreamExecution$$runBatches$1.apply$mcZ$sp(StreamExecution.scala:244)
        at org.Apache.spark.sql.execution.streaming.ProcessingTimeExecutor.execute(TriggerExecutor.scala:43)
        at org.Apache.spark.sql.execution.streaming.StreamExecution.org$Apache$spark$sql$execution$streaming$StreamExecution$$runBatches(StreamExecution.scala:239)
        at org.Apache.spark.sql.execution.streaming.StreamExecution$$anon$1.run(StreamExecution.scala:177)
Caused by: Java.io.NotSerializableException: org.Apache.spark.sql.execution.streaming.StreamExecution
Serialization stack:
        - object not serializable (class: org.Apache.spark.sql.execution.streaming.StreamExecution, value: Streaming Query [id = 9b01db99-9120-4047-b779-2e2e0b289f65, runId = e20beefa-146a-4139-96f9-de3d64ce048a] [state = TERMINATED])
        - field (class: $line21.$read$$iw$$iw, name: query, type: interface org.Apache.spark.sql.streaming.StreamingQuery)
        - object (class $line21.$read$$iw$$iw, $line21.$read$$iw$$iw@24747e0f)
        - field (class: $line21.$read$$iw, name: $iw, type: class $line21.$read$$iw$$iw)
        - object (class $line21.$read$$iw, $line21.$read$$iw@1814ed19)
        - field (class: $line21.$read, name: $iw, type: class $line21.$read$$iw)
        - object (class $line21.$read, $line21.$read@13e62f5d)
        - field (class: $line25.$read$$iw, name: $line21$read, type: class $line21.$read)
        - object (class $line25.$read$$iw, $line25.$read$$iw@14240e5c)
        - field (class: $line25.$read$$iw$$iw, name: $outer, type: class $line25.$read$$iw)
        - object (class $line25.$read$$iw$$iw, $line25.$read$$iw$$iw@11e4c6f5)
        - field (class: $line25.$read$$iw$$iw$JDBCSink, name: $outer, type: class $line25.$read$$iw$$iw)
        - object (class $line25.$read$$iw$$iw$JDBCSink, $line25.$read$$iw$$iw$JDBCSink@6c096c84)
        - field (class: org.Apache.spark.sql.execution.streaming.ForeachSink, name: org$Apache$spark$sql$execution$streaming$ForeachSink$$writer, type: class org.Apache.spark.sql.ForeachWriter)
        - object (class org.Apache.spark.sql.execution.streaming.ForeachSink, org.Apache.spark.sql.execution.streaming.ForeachSink@6feccb75)
        - field (class: org.Apache.spark.sql.execution.streaming.ForeachSink$$anonfun$addBatch$1, name: $outer, type: class org.Apache.spark.sql.execution.streaming.ForeachSink)
        - object (class org.Apache.spark.sql.execution.streaming.ForeachSink$$anonfun$addBatch$1, <function1>)
        at org.Apache.spark.serializer.SerializationDebugger$.improveException(SerializationDebugger.scala:40)
        at org.Apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:46)
        at org.Apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:100)
        at org.Apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:295)
        ... 25 more

それを機能させる方法は?

[〜#〜]ソリューション[〜#〜]

(すべてのおかげで、簡単な解決策のための@zsxwingへの特別な感謝):

  1. JDBCSinkクラスをファイルに保存します。
  2. Spark-Shellでクラスf.egをロードします。 scala> :load <path_to_a_JDBCSink.scala_file>を使用
  3. 最後に、JDBCSinkクラス定義なしのscala> :pasteコード。
13
Lukiz

JDBCSinkを、外部参照をキャプチャする可能性のある内部クラスとして定義するのではなく、別のファイルで定義するだけです。

4
zsxwing

誰かがインタラクティブなワークブックでこれに遭遇した場合、この解決策も機能します:

JDBCSinkclassを別のファイルに保存する代わりに、同じワークブック内で個別のパッケージ(「パッケージセル」)として宣言し、そのパッケージを、使用しているセルにインポートすることもできます。ここで詳しく説明 https://docs.databricks.com/user-guide/notebooks/package-cells.html

0
silent

ここでの違反者はJDBCSinkクラス内でのimport spark.implicits._の使用であるように見えます:

  • JDBCSinkはシリアライズ可能でなければなりません
  • このインポートを追加することで、JDBCSink参照をシリアル化できないSparkSessionにして、それとともにシリアル化されます(技術的にはSparkSession extends Serializableですが、ワーカーノード)

良いニュース:このインポートを使用していないので、removeを使用するだけで動作します。

0
Tzach Zohar