繁体   English   中英

Flink - 将 Avro 数据流转换为表

[英]Flink - Convert Avro datastream to table

我在 Kafka 中有 Avro 格式的消息。 这些必须转换为表格并使用SQL进行选择,然后转换为stream,最后下沉。 有多个具有不同 Avro 模式的 Kafka 主题,因此需要动态表。

这是我正在使用的代码

StreamExecutionEnvironment env = ...;
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

FlinkKafkaConsumer<MyAvroClass> kafkaConsumer = ...;
var kafkaInputStream = env.addSource(kafkaConsumer, "kafkaInput");

Table table = tableEnv.fromDataStream(kafkaInputStream);
tableEnv.executeSql("DESCRIBE " + table).print();
...

MyAvroClass是 Avro class,它扩展了SpecificRecordBase并包含一个数组。
此 class 的代码。

public class MyAvroClass extends SpecificRecordBase implements SpecificRecord {
  // avro fields
  private String event_id;
  private User user;
  private List<Item> items; 
  
  // getter, setters, constructors, builders, ...
}

我无法访问items字段的元素。 当我打印表描述时,我看到项目是 ANY 类型

+------------+-------------------------------------------------------------+------+-----+--------+-----------+
|       name |                                                        type | null | key | extras | watermark |
+------------+-------------------------------------------------------------+------+-----+--------+-----------+
|   event_id |                                                      STRING | true |     |        |           |
|      items |                        LEGACY('RAW', 'ANY<java.util.List>') | true |     |        |           |
|       user |  LEGACY('STRUCTURED_TYPE', 'POJO<com.company.events.User>') | true |     |        |           |
+------------+-------------------------------------------------------------+------+-----+--------+-----------+  

如何将其转换为可用于查询 from 项目的类型? 提前致谢

我目前正在为此目的使用这种方法。

public static <T extends SpecificRecord> Table toTable(StreamTableEnvironment tEnv,
                                                       DataStream<T> dataStream,
                                                       Class<T> cls) {
  RichMapFunction<T, Row> avroSpecific2RowConverter = new RichMapFunction<>() {
    private transient AvroSerializationSchema<T> avro2bin = null;
    private transient AvroRowDeserializationSchema bin2row = null;

    @Override
    public void open(Configuration parameters) throws Exception {
      avro2bin = AvroSerializationSchema.forSpecific(cls);
      bin2row = new AvroRowDeserializationSchema(cls);
    }

    @Override
    public Row map(T value) throws Exception {
      byte[] bytes = avro2bin.serialize(value);
      Row row = bin2row.deserialize(bytes);
      return row;
    }
  };

  SingleOutputStreamOperator<Row> rows = dataStream.map(avroSpecific2RowConverter)
    // https://issues.apache.org/jira/browse/FLINK-23885
    .returns(AvroSchemaConverter.convertToTypeInfo(cls));

  return tEnv.fromDataStream(rows);
}

我遇到了类似的问题,尽管官方支持 Flink Table 的类型插值无法获取 java.util.List 或 java.util.Map。 我找到了一个解决方法(阅读:HACK)我想分享。

第 1 步:将数据映射到 POJO 时,坚持使用您知道会正确插值的字段。 在我的例子中,我有 Map<String, String> 插值失败LEGACY('RAW', ANY<java.util.Map>) 我将它连接成一个字符串(例如,逗号分隔的条目,其中每个条目都是“key:value”。它们连接成一个字符串)。

第 2 步:对于您的输入数据 stream,确保将其转换为 DataStream[MY_POJO_TYPE]。

第 3 步:Go 提前执行Table table = tableEnv.fromDataStream(kafkaInputStream); 照常。

第 4 步:使用ScalarFunction对表执行另一个转换。 在我的例子中,我编写了一个用户定义的标量 function 和 output Map<String, String>。 奇怪的是,当在表抽象中插入 Map 数据时,Flink 能够将类型正确地插入到 Flink MAP 类型中。

这是用户定义的标量 function 的粗略示例(在 Java 中):

import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.flink.table.functions.ScalarFunction;

public class TagsMapTypeScalarFunction extends ScalarFunction {

  // See
  // https://nightlies.apache.org/flink/flink-docs-master/docs/dev/table/functions/udfs/#scalar-functions for reference implementation and how interfacing with ScalarFunction works.
  public Map<String, String> eval(String s) {
    // input is comma delimited key:value pairs.
    return Arrays.stream(s.split(","))
        .filter(kv -> kv != "")
        .map(kv -> kv.split(":"))
        .filter(pair -> pair.length == 2)
        .filter(pair -> Arrays.stream(pair).allMatch(token -> token != ""))
        .collect(Collectors.toMap(pair -> pair[0].trim(), pair -> pair[1].trim()));
  }
}

这是调用的大致样子(在 Scala 中):


    //This table has a field "tags" which is the comma-delimited, key:value squished string.
    val transformedTable = tableEnv.fromDataStream(kafkaInputStream: DataStream[POJO])

    tableEnv.createTemporaryFunction(
      "TagsMapTypeScalarFunction",
      classOf[TagsMapTypeScalarFunction]
    );

    val anotherTransform =
      transformedTable
        .select($"*", call("TagsMapTypeScalarFunction", $"tags").as("replace_tags"))
        .dropColumns($"tags")
        .renameColumns($"replace_tags".as("tags"))

    anotherTransform

从 map 转换为字符串,然后返回为 map 确实是一项“繁忙”的工作。但总比被卡住好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM