Spark(2.2):使用结构化流式传输来从Kafka反序列化Thrift记录

时间:2017-10-19 07:12:08

标签: scala apache-spark spark-streaming thrift

我是新来的火花。我使用结构化流来从kafka读取数据。

我可以在Scala中使用此代码读取数据:

val data = spark.readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", brokers)
      .option("subscribe", topics)
      .option("startingOffsets", startingOffsets) 
      .load()

我在价值列中的数据是Thrift记录。 Streaming api以二进制格式提供数据。我看到将数据转换为字符串或json的示例,但我无法找到有关如何将数据反序列化为Thrift的任何示例。

我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:1)

嗯,这是后续解决方案。我无法发布自己的代码,但这里是您可以使用的公共代码,信用给所有者/编码员。

https://github.com/airbnb/airbnb-spark-thrift/blob/master/src/main/scala/com/airbnb/spark/thrift/

首先,你需要通过调用convertObject函数将数组[byte] / value转换为Row,让我们称之为makeRow

其次,您需要通过调用convert函数来获取您的thrift类structType / schema,让我们调用最终结果schema

然后你需要注册一个像这样的val deserializer = udf((bytes: Array[Byte]) => makeRow(bytes), schema)

的UDF

注意:如果不传递架构,就无法使用makeRow,否则Spark会抱怨:Schema for type org.apache.spark.sql.Row is not supported

然后你可以这样使用它:

val stuff = kafkaStuff.withColumn("data", deserializer(kafkaStuff("value"))) val finalStuff = stuff.select("data.*")

而且......你已经完成了! 希望这会有所帮助。

再次赞扬这篇文章Spark UDF for StructType / Row 当我以前的解决方案如此接近时,这给了我最后的想法。

答案 1 :(得分:0)

我在databricks网站上找到了这个博客。它展示了如何利用Spark SQL的API来使用和转换来自Apache Kafka的复杂数据流。

https://databricks.com/blog/2017/04/26/processing-data-in-apache-kafka-with-structured-streaming-in-apache-spark-2-2.html

有一节解释了如何将UDF用于反序列化行:

object MyDeserializerWrapper {
  val deser = new MyDeserializer
}
spark.udf.register("deserialize", (topic: String, bytes: Array[Byte]) => 
  MyDeserializerWrapper.deser.deserialize(topic, bytes)
)

df.selectExpr("""deserialize("topic1", value) AS message""")

我正在使用java,因此必须编写以下示例UDF,以检查它在java中的调用方式:

UDF1<byte[], String> mode = new UDF1<byte[], String>() {
            @Override
            public String call(byte[] bytes) throws Exception {
                String s = new String(bytes);
                return "_" + s;
            }
        };

现在我可以在结构化流式字数统计示例中使用此UDF,如下所示:

Dataset<String> words = df
                //converted the DataFrame to a Dataset of String using .as(Encoders.STRING())
//                .selectExpr("CAST(value AS STRING)")
                .select( callUDF("mode", col("value")) )
                .as(Encoders.STRING())
                .flatMap(
                        new FlatMapFunction<String, String>() {
                            @Override
                            public Iterator<String> call(String x) {
                                return Arrays.asList(x.split(" ")).iterator();
                            }
                        }, Encoders.STRING());

我的下一步是为thrift反序列化编写一个UDF。我会在完成后立即发布。