【问题标题】:Google Dataflow: how to parse big file with valid JSON array from FileIO.ReadableFileGoogle Dataflow:如何使用 FileIO.ReadableFile 中的有效 JSON 数组解析大文件
【发布时间】:2018-02-05 08:25:06
【问题描述】:


在我的管道中,FileIO.readMatches() 转换读取带有有效 JSON 数组的大 JSON 文件(大约 300-400MB)并将FileIO.ReadableFile 对象返回到下一个转换。我的任务是从该 JSON 数组中读取每个 JSON 对象,添加新属性并输出到下一个转换。

目前我解析 JSON 文件的代码如下所示:

        // file is a FileIO.ReadableFile object 
        InputStream bis = new ByteArrayInputStream(file.readFullyAsBytes());
        // Im using gson library to parse JSON
        JsonReader reader = new JsonReader(new InputStreamReader(bis, "UTF-8"));
        JsonParser jsonParser = new JsonParser();
        reader.beginArray();
        while (reader.hasNext()) {
            JsonObject jsonObject = jsonParser.parse(reader).getAsJsonObject();
            jsonObject.addProperty("Somename", "Somedata");
            // processContext is a ProcessContext object
            processContext.output(jsonObject.toString());
        }
        reader.close();

在这种情况下,文件的全部内容将在我的记忆中,这带来了获取 java.lang.OutOfMemoryError 的选项。我正在寻找解决方案来一一读取所有 JSON 对象,而无需将整个文件保存在我的内存中。 可能的解决方案是使用来自对象FileIO.ReadableFile 的方法open(),它返回ReadableByteChannel 通道,但我不确定如何使用该通道专门从该通道读取一个JSON 对象。

更新的解决方案 这是我更新的解决方案,逐行读取文件

    ReadableByteChannel readableByteChannel = null;
    InputStream inputStream = null;
    BufferedReader bufferedReader = null;
    try {
        // file is a FileIO.ReadableFile 
        readableByteChannel = file.open();
        inputStream = Channels.newInputStream(readableByteChannel);
        bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            if (line.length() > 1) {
                // my final output should contain both filename and line
                processContext.output(fileName + file);
            }
        }
    } catch (IOException ex) {
        logger.error("Exception during reading the file: {}", ex);
    } finally {
        IOUtils.closeQuietly(bufferedReader);
        IOUtils.closeQuietly(inputStream);
    }

我发现此解决方案不适用于在 n1-standard-1 机器上运行的 Dataflow,并引发 java.lang.OutOfMemoryError: GC overhead limit exceeded 异常并在 n1-standard-2 机器上正常工作。

【问题讨论】:

  • 整个 JSON 文件是否需要在一个转换步骤中进行解析?您可以使用TextIO 类来读取 JSON 文件并单独解析每个 JSON 数组,而不必将整个文件保存在内存中。您能否提供更多关于如何在代码示例中定义管道的上下文?
  • 不幸的是,我不能在我的解决方案中使用 TextIO,因为我需要在同一个转换步骤中使用文件名和文件内容。我问了这个问题here。在我目前的帖子中,我没有提到那部分来简化我的问题。我的想法是获取 FileIO.ReadableFile 作为我的转换步骤的输入,从该输入对象中提取文件名和文件内容,从文件中解析每一行并将该文件与文件名中的附加信息一起输出到下一个转换步骤。

标签: java json google-cloud-dataflow apache-beam google-cloud-pubsub


【解决方案1】:

ReadableByteChannel 是一个 Java NIO API,在 Java 7 中引入。Java provides a way 将其转换为 InputStream:InputStream bis = Channels.newInputStream(file.open()); - 我相信这是您需要进行的唯一更改。

【讨论】:

  • 您好 jkff,感谢您的回复。其实我已经按照你说的做了:我逐行读取我的输入流并输出到下一个变换。我在具有大约 500 MB 堆内存的 n1-standard-1 机器上运行我的应用程序。但它看起来还不够,因为我从processContext.output(message) 行看到了java.lang.OutOfMemoryError: GC overhead limit exceeded 异常。我预计最低的 n1-standard-1 机器应该足以进行这种转换。你有什么意见?是我的代码还是机器太低了?
  • 您发布的代码使用 new ByteArrayInputStream(file.readFullyAsBytes()) 读取内存中的整个文件。你能发布你更新的代码吗?
猜你喜欢
  • 2015-04-08
  • 1970-01-01
  • 1970-01-01
  • 2018-07-24
  • 1970-01-01
  • 2017-02-03
  • 2015-09-15
  • 2019-03-02
  • 1970-01-01
相关资源
最近更新 更多