簡體   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