Spark: чтение сообщений Avro из Kafka с помощью Spark Scala

Я пытаюсь выполнить приведенный ниже код в spark 2.4.3, чтобы читать сообщения Avro от kafka.

Схема сохраняется в confluent schema registry, когда данные публикуются на kafka. Я пробовал несколько решений, которые уже обсуждались здесь (Интеграция структурированной потоковой передачи Spark с реестром конфлюентной схемы / Чтение сообщений Avro из Kafka с помощью Spark 2.0.2 (структурированная потоковая передача)), но не смог заставить его работать. Или я не мог найти правильный способ сделать это, особенно когда схема хранится в каком-то Schema Registry.

Вот текущий код, который я пробую, где, по крайней мере, я могу получить какой-то результат, но все записи выходят как значения null. Собственно в теме есть данные. Может ли кто-нибудь помочь мне в этом?

import io.confluent.kafka.schemaregistry.client.{CachedSchemaRegistryClient, SchemaRegistryClient}
import io.confluent.kafka.serializers.AbstractKafkaAvroDeserializer
import org.apache.avro.Schema
import org.apache.avro.generic.GenericRecord
import org.apache.spark.sql.avro.SchemaConverters

object ScalaSparkAvroConsumer {

    private val topic = "customer.v1"
    private val kafkaUrl = "localhost:9092"
    private val schemaRegistryUrl = "http://127.0.0.1:8081"

    private val schemaRegistryClient = new CachedSchemaRegistryClient(schemaRegistryUrl, 128)
    private val kafkaAvroDeserializer = new AvroDeserializer(schemaRegistryClient)

    private val avroSchema = schemaRegistryClient.getLatestSchemaMetadata(topic + "-value").getSchema
    private var sparkSchema = SchemaConverters.toSqlType(new Schema.Parser().parse(avroSchema))

    def main(args: Array[String]): Unit = {
      val spark = getSparkSession()

      spark.sparkContext.setLogLevel("ERROR")

      spark.udf.register("deserialize", (bytes: Array[Byte]) =>
        DeserializerWrapper.deserializer.deserialize(bytes)
      )

      val df = spark
        .readStream
        .format("kafka")
        .option("kafka.bootstrap.servers", kafkaUrl)
        .option("subscribe", topic)
        .option("startingOffsets", "earliest")
        .load()

      val valueDataFrame = df.selectExpr("""deserialize(value) AS message""")

      import org.apache.spark.sql.functions._

      val formattedDataFrame = valueDataFrame.select(
        from_json(col("message"), sparkSchema.dataType).alias("parsed_value"))
        .select("parsed_value.*")

      formattedDataFrame
        .writeStream
        .format("console")
        .option("truncate", false)
        .start()
        .awaitTermination()
    }

    object DeserializerWrapper {
      val deserializer = kafkaAvroDeserializer
    }

    class AvroDeserializer extends AbstractKafkaAvroDeserializer {
      def this(client: SchemaRegistryClient) {
        this()
        this.schemaRegistry = client
      }

      override def deserialize(bytes: Array[Byte]): String = {
        val genericRecord = super.deserialize(bytes).asInstanceOf[GenericRecord]
        genericRecord.toString
      }
    }
}

Получение вывода, как показано ниже:

-------------------------------------------
Batch: 0
-------------------------------------------
+------+-------+
|header|control|
+------+-------+
|null  |null   |
|null  |null   |
|null  |null   |
|null  |null   |
+------+-------+
only showing top 20 rows        

person Leibnitz    schedule 26.11.2019    source источник
comment
Я уже пробовал их, как указано в описании, и не смог заставить их работать. Не могли бы вы посоветовать мне это?   -  person Leibnitz    schedule 27.11.2019
comment
Я написал там ответ и могу убедиться, что он у меня сработал. Если вы получите значение null, вероятно, сгенерированная схема не соответствует содержимому записи. Ответ там не использовал .asInstanceOf[GenericRecord], например   -  person OneCricketeer    schedule 28.11.2019
comment
Вы можете проверить, что внутри valueDataFrame? Ты умеешь valueDataFrame.writeStream.format("console")? И чтобы упростить отладку, используйте read (Spark SQL), а не readStream (структурированная потоковая передача), пока он не даст вам правильные значения.   -  person Jacek Laskowski    schedule 28.11.2019
comment
Да, я использовал read, и это дало мне фактическое сообщение, как это. {"header": {"Id": "123"},"control": {"subject": "EOD"}}   -  person Leibnitz    schedule 28.11.2019


Ответы (1)


Интеграция сериализации Avro, сервера схемы Kafka и потоковой передачи Spark с from_confluence_avro () сделает вашу жизнь проще. Вы можете найти это здесь:

https://github.com/AbsaOSS/ABRiS

person Abdulrahman    schedule 29.01.2020