私はSparkにいます、AvroファイルからのRDDを持っています。そのRDDでいくつかの変換を行い、Avroファイルとして保存し直したいと思います。
val job = new Job(new Configuration())
AvroJob.setOutputKeySchema(job, getOutputSchema(inputSchema))
rdd.map(elem => (new SparkAvroKey(doTransformation(elem._1)), elem._2))
.saveAsNewAPIHadoopFile(outputPath,
classOf[AvroKey[GenericRecord]],
classOf[org.Apache.hadoop.io.NullWritable],
classOf[AvroKeyOutputFormat[GenericRecord]],
job.getConfiguration)
これを実行すると、Sparkは、Schema $ recordSchemaがシリアル化できないと文句を言います。
.map呼び出しのコメントを解除すると(そしてrdd.saveAsNewAPIHadoopFileがあるだけで)、呼び出しは成功します。
私はここで何が間違っているのですか?
何か案が?
ここでの問題は、ジョブで使用されるavro.Schemaクラスの非直列化可能性に関連しています。 map関数内のコードからスキーマオブジェクトを参照しようとすると、例外がスローされます。
たとえば、次のように実行しようとすると、 "Task not serializable"例外が発生します。
val schema = new Schema.Parser().parse(new File(jsonSchema))
...
rdd.map(t => {
// reference to the schema object declared outside
val record = new GenericData.Record(schema)
})
関数ブロック内にスキーマの新しいインスタンスを作成するだけで、すべてを機能させることができます。
val schema = new Schema.Parser().parse(new File(jsonSchema))
// The schema above should not be used in closures, it's for other purposes
...
rdd.map(t => {
// create a new Schema object
val innserSchema = new Schema.Parser().parse(new File(jsonSchema))
val record = new GenericData.Record(innserSchema)
...
})
処理するすべてのレコードのavroスキーマを解析したくないので、より良い解決策は、パーティションレベルでスキーマを解析することです。以下も機能します。
val schema = new Schema.Parser().parse(new File(jsonSchema))
// The schema above should not be used in closures, it's for other purposes
...
rdd.mapPartitions(tuples => {
// create a new Schema object
val innserSchema = new Schema.Parser().parse(new File(jsonSchema))
tuples.map(t => {
val record = new GenericData.Record(innserSchema)
...
// this closure will be bundled together with the outer one
// (no serialization issues)
})
})
上記のコードは、jsonSchemaファイルへの移植可能な参照を提供する限り機能します。これは、map関数が複数のリモートエグゼキューターによって実行されるためです。これは、HDFS内のファイルへの参照にすることも、JAR内のアプリケーションと一緒にパッケージ化することもできます(後者の場合、クラスローダー関数を使用してその内容を取得します)。
SparkでAvroを使用しようとしている場合は、まだ未解決のコンパイルの問題がいくつかあり、MavenPOMで次のインポートを使用する必要があることに注意してください。
<dependency>
<groupId>org.Apache.avro</groupId>
<artifactId>avro-mapred</artifactId>
<version>1.7.7</version>
<classifier>hadoop2</classifier>
<dependency>
"hadoop2"
分類子に注意してください。問題は https://issues.Apache.org/jira/browse/SPARK-3039 で追跡できます。
Sparkで使用されるデフォルトのシリアライザーはJavaシリアル化です。したがって、すべてのJavaタイプについて、Javaシリアル化を使用してシリアル化を試みます。 AvroKeyはシリアル化できないため、エラーが発生します。
KryoSerializer、またはカスタムシリアル化(Avroなど)のプラグインを使用できます。シリアル化について詳しくは、こちらをご覧ください。 http://spark-project.org/docs/latest/tuning.html
外部化可能なものでオブジェクトをラップすることもできます。たとえば、AvroFlumeEventをラップするSparkFlumeEventをここで確認してください: https://github.com/Apache/spark/blob/master/external/flume/src/main/scala/org/Apache/spark/streaming/flume /FlumeInputDStream.scala
データフレームを使用すると、databricsライブラリを使用してavroを作成するのは非常に簡単です。
dataframe.write.format( "com.databricks.spark.avro")。avro($ hdfs_path)
あなたの場合、入力はavroであるため、スキーマが関連付けられているため、avroをデータフレームに直接読み込むことができ、変換後、上記のコードを使用してavroに書き込むことができます。
Avroをデータフレームに読み込むには:
Spark 1.6
val dataframe = sqlContext.read.avro($ hdfs_path)OR val dataframe = sqlContext.read.format( "com.databricks.spark.avro")。load($ hdfs_path)
Spark 2.1
val dataframe = sparkSession.read.avro($ hdfs_path)OR val dataframe = sparkSession.read.format( "com.databricks.spark.avro")。load($ hdfs_path)