Java 如何使用AvroParquetWriter并通过AmazonS3 api写入S3?

Java 如何使用AvroParquetWriter并通过AmazonS3 api写入S3?,java,hadoop,amazon-s3,avro,parquet,Java,Hadoop,Amazon S3,Avro,Parquet,我目前正在使用下面的代码通过Avro编写拼花地板。这段代码将其写入文件系统,但我想写入S3 try { StopWatch sw = StopWatch.createStarted(); Schema avroSchema = AvroSchemaBuilder.build("pojo", message.getTransformedMessage().get(0)); final String parquetFile = "parquet/data.parquet";

我目前正在使用下面的代码通过Avro编写拼花地板。这段代码将其写入文件系统,但我想写入S3

try {
    StopWatch sw = StopWatch.createStarted();
    Schema avroSchema = AvroSchemaBuilder.build("pojo", message.getTransformedMessage().get(0));
    final String parquetFile = "parquet/data.parquet";
    final Path path = new Path(parquetFile);

    ParquetWriter writer = AvroParquetWriter.<GenericData.Record>builder(path)
        .withSchema(avroSchema)
        .withConf(new org.apache.hadoop.conf.Configuration())
        .withCompressionCodec(CompressionCodecName.SNAPPY)
        .withWriteMode(Mode.OVERWRITE)//probably not good for prod. (overwrites files).
        .build();

    for (Map<String, Object> row : message.getTransformedMessage()) {
      StopWatch stopWatch = StopWatch.createStarted();
      final GenericRecord record = new GenericData.Record(avroSchema);
      row.forEach((k, v) -> {
        record.put(k, v);
      });
      writer.write(record);
    }
    //todo:  Write to S3.  We should probably write via the AWS objects.  This does not show that.
    //https://stackoverflow.com/questions/47355038/how-to-generate-parquet-file-using-pure-java-including-date-decimal-types-an
    writer.close();
    System.out.println("Total Time: " + sw);

  } catch (Exception e) {
    //do somethign here.  retryable?  non-retryable?  Wrap this excetion in one of these?
    transformedParquetMessage.getOriginalMessage().getMetaData().addException(e);
  }
试试看{
StopWatch sw=StopWatch.createStarted();
模式avroSchema=AvroSchemaBuilder.build(“pojo”,message.getTransformedMessage().get(0));
最终字符串parquetFile=“parquet/data.parquet”;
最终路径=新路径(拼花文件);
ParquetWriter writer=AvroParquetWriter.builder(路径)
.withSchema(avroSchema)
.withConf(新org.apache.hadoop.conf.Configuration())
.withCompressionCodec(CompressionCodecName.SNAPPY)
.withWriteMode(Mode.OVERWRITE)//可能不适合生产(覆盖文件)。
.build();
对于(映射行:message.getTransformedMessage()){
StopWatch StopWatch=StopWatch.createStarted();
最终GenericRecord记录=新的GenericData.record(avroSchema);
每行((k,v)->{
记录。放置(k,v);
});
写作(记录);
}
//todo:写入S3。我们可能应该通过AWS对象进行写入。这并没有显示这一点。
//https://stackoverflow.com/questions/47355038/how-to-generate-parquet-file-using-pure-java-including-date-decimal-types-an
writer.close();
系统输出打印项次(“总时间:+sw”);
}捕获(例外e){
//在这里做点什么。可回收的?不可回收的?用其中一个包裹这个例外?
transformedParquetMessage.getOriginalMessage().getMetaData().addException(e);
}
这可以很好地写入文件,但是如何让它流式传输到AmazonS3API中呢?我在网上发现了一些使用Hadoop aws jar的代码,但这需要一些Windows exe文件才能工作,当然,我们希望避免这种情况。目前我只使用:

 <dependency>
  <groupId>org.apache.avro</groupId>
  <artifactId>avro</artifactId>
  <version>1.9.2</version>
</dependency>
<dependency>
  <groupId>org.apache.parquet</groupId>
  <artifactId>parquet-avro</artifactId>
  <version>1.8.1</version>
</dependency>
<dependency>
  <groupId>org.apache.hadoop</groupId>
  <artifactId>hadoop-core</artifactId>
  <version>1.2.1</version>
</dependency>

org.apache.avro
阿夫罗
1.9.2
org.apache.parquet
镶木地板
1.8.1
org.apache.hadoop
hadoop内核
1.2.1

问题是,有没有办法截取AvroParquetWriter上的输出流,以便将其流式传输到S3?我想这样做的主要原因是为了重试。S3自动重试最多3次。这将对我们有很大帮助。

希望我没有误解这个问题,但这里您正在做的似乎是将avro转换为拼花地板,并且您希望将拼花地板上传到s3

关闭ParquetWriter后,您应该调用如下所示的方法(假定该方法不会拦截从avro到parquet的流写入,它只是流式传输不再写入的parquet文件):

使用AWS SDK

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk</artifactId>
    <version>1.11.749</version>
</dependency>

亚马逊网站
aws java sdk
1.11.749
当然,该方法将驻留在不同的utils类中,并且该方法的构造函数应该使用凭据初始化AmazonS3 s3Client,因此您所需要做的就是调用并访问它的s3Client成员以放置对象


希望这有帮助

这取决于hadoop aws jar,所以如果你不愿意使用它,我不确定我能帮到你。然而,我在mac电脑上运行,没有任何windows exe文件,所以我不确定你说的这些文件来自哪里。AvroParquetWriter已经依赖于Hadoop,因此,即使这种额外的依赖性对您来说是不可接受的,但对其他人来说也可能没什么大不了的:

您可以使用AvroParquetWriter直接流式传输到S3,方法是向其传递使用URI参数创建的Hadoop路径,并设置适当的配置

val uri = new URI("s3a://<bucket>/<key>")
val path = new Path(uri)

val config = new Configuration()
config.set("fs.s3a.access.key", key)
config.set("fs.s3a.secret.key", secret)
config.set("fs.s3a.session.token", sessionToken)
config.set("fs.s3a.aws.credentials.provider", credentialsProvider)

val writer = AvroParquetWriter.builder[GenericRecord](path).withConf(config).withSchema(schema).build()

也许是这样吧?尽管它使用了您希望避免的libs/exe:(:)是的,这很接近,但它依赖于服务器上运行的Hadoop。我担心这不是真正可行的。如果你看一看这个类,你会发现这个超类是org.apache.parquet.hadoop.ParquetWriter,所以我会说(不要深入挖掘)存在一个配置好的hadoop是这个类的一个必要条件。看来Spark是一个很好的选择。谢谢你的回复!我真的在努力克服两件事:hadoop的外部依赖和S3依赖。如果拼花只是一种文件格式,为什么写出来这么难?!你可以找到一个类似问题的答案——看看链接的Jira问题。这是一个很好的尝试,但这只是展示了如何将文件上传到S3。Hadoop使这变得很困难,因为文件首先保存到磁盘。我想直接从ParquetWriter转到s3。@markthegrea Hadoop使用HDFS来模拟POSIX文件系统的行为,而s3是一个对象存储,而不是一个文件系统-因此,要直接写入s3,您必须配置集群HDFS配置以与s3同步-但是,您的Hadoop作业仍将利用Hadoop API的HDFS实现,您将在集群磁盘空间上进行编写。如果您使用AWS EMR-此同步已设置-并且您可以将对象从HDFS推送到s3。在避免写入HDFS的情况下,您的用例是什么?我们只是将数据转换为拼花地板。如上所示,parquetWriter只写入本地文件系统。我们需要在不涉及hadoop的情况下写入s3。@标记Thegrea如果没有包装器,您无法直接写入s3。包装器正在修改hadoop API HDFS实现,以便通过基于s3路径(m4gic提供了链接)创建一些sudo文件系统树来使用对象存储(s3)。Hadoop的想法是使用HDFS,因为在map reduce期间,您可以在本地检索存储的块,而洗牌是内部的,Thread可以在其中管理它。我想,我试图理解的是,为什么要通过这种努力来使用从长远来看可能更难维护的包装器?除非需要考虑磁盘空间,否则这是正确的答案,但需要充实。请包括你用过的罐子。我们这样做,发现只有Windows需要额外的.exe文件等。Linux(AWS上的fargate)没有。AWS和文档对这些JAR进行了更新和支持
val uri = new URI("s3a://<bucket>/<key>")
val path = new Path(uri)

val config = new Configuration()
config.set("fs.s3a.access.key", key)
config.set("fs.s3a.secret.key", secret)
config.set("fs.s3a.session.token", sessionToken)
config.set("fs.s3a.aws.credentials.provider", credentialsProvider)

val writer = AvroParquetWriter.builder[GenericRecord](path).withConf(config).withSchema(schema).build()
"org.apache.avro" % "avro" % "1.8.1"
"org.apache.hadoop" % "hadoop-common" % "2.9.0"
"org.apache.hadoop" % "hadoop-aws" % "2.9.0"
"org.apache.parquet" % "parquet-avro" % "1.8.1"