Dataflow输出参数化类型为avro文件

时间:2016-06-17 16:58:29

标签: google-cloud-dataflow

我有一个成功输出Avro文件的管道,如下所示:

@DefaultCoder(AvroCoder.class)
class MyOutput_T_S {
  T foo;
  S bar;
  Boolean baz;
  public MyOutput_T_S() {}
}

@DefaultCoder(AvroCoder.class)
class T {
  String id;
  public T() {}
}

@DefaultCoder(AvroCoder.class)
class S {
  String id;
  public S() {}
}
...
PCollection<MyOutput_T_S> output = input.apply(myTransform);
output.apply(AvroIO.Write.to("/out").withSchema(MyOutput_T_S.class));

如何使用参数化输出MyOutput<T, S>(其中TS都是使用反射的Avro代码可以重现此确切行为。)

主要问题是Avro反射不适用于参数化类型。所以基于这些回答:

1)我想我需要写一个自定义的CoderFactory但是,我很难弄清楚它是如何工作的(我很难找到例子)。奇怪的是,一个完全天真的编码器工厂似乎让我运行管道并使用DataflowAssert检查正确的输出:

cr.RegisterCoder(MyOutput.class, new CoderFactory() {
  @Override
  public Coder<?> create(List<? excents Coder<?>> componentCoders) {
    Schema schema = new Schema.Parser().parse("{\"type\":\"record\,"
      + "\"name\":\"MyOutput\","
      + "\"namespace\":\"mypackage"\","
      + "\"fields\":[]}"
    return AvroCoder.of(MyOutput.class, schema);
  }
  @Override
  public List<Object> getInstanceComponents(Object value) {
    MyOutput<Object, Object> myOutput = (MyOutput<Object, Object>) value;
    List components = new ArrayList();
    return components;
  }

虽然我现在可以成功断言输出,但我希望这不会因为写入文件而削减它。我还没弄明白我应该如何使用提供的componentCoders来生成正确的架构,如果我尝试将TS的架构推送到{{1我得到了:

fields

2)假设我弄清楚如何编码java.lang.IllegalArgumentException: Unable to get field id from class null 。我将什么传递给MyOutput?如果我传递了AvroIO.Write.withSchema或模式,我会收到类型不匹配错误。

1 个答案:

答案 0 :(得分:1)

我认为有两个问题(如果我错了,请纠正我):

  1. 如何启用编码器注册表以为MyOutput<T, S>的各种参数化提供编码器?
  2. 如何使用MyOutput<T, S> AvroIO.Write对文件进行值{。}}。{/ li>

    第一个问题是通过在您找到的链接问题中注册CoderFactory来解决。

    您的天真编码器可能允许您毫无问题地运行管道,因为序列化正在被优化掉。当然,没有字段的Avro架构将导致这些字段在序列化+反序列化往返中被删除。

    但假设您使用字段填写架构,您对CoderFactory#create的处理方式看起来正确。我不知道邮件的确切原因java.lang.IllegalArgumentException: Unable to get field id from class null,但是AvroCoder.of(MyOutput.class, schema)的调用应该适用于适当汇编的schema。如果这有问题,更多细节(例如堆栈轨道的其余部分)将会有所帮助。

    但是,覆盖CoderFactory#getInstanceComponents应该返回一个值列表,每个类型参数MyOutput一个。像这样:

    @Override
    public List<Object> getInstanceComponents(Object value) {
      MyOutput<Object, Object> myOutput = (MyOutput<Object, Object>) value;
      return ImmutableList.of(myOutput.foo, myOutput.bar);
    }
    

    第二个问题可以使用与第一个相同的支持代码来回答,但在其他方面是独立的。 AvroIO.Write.withSchema始终显式使用提供的架构。它确实使用了AvroCoder,但这实际上是一个实现细节。只需提供兼容的架构即可 - 必须为要输出T的{​​{1}}和S的每个值组合架构。