只有在完全写入和关闭后才能从HDFS读取文件

时间:2013-12-10 09:19:07

标签: java io hdfs java-io

我有两个进程在运行。一种是将文件写入HDFS,另一种是加载这些文件。

第一个进程(写入文件的进程)正在使用:

private void writeFileToHdfs(byte[] sourceStream, Path outFilePath) {
FSDataOutputStream out = null;
try {
    // create the file
    out = getFileSystem().create(outFilePath);
    out.write(sourceStream);
} catch (Exception e) {
    LOG.error("Error while trying to write a file to hdfs", e);
} finally {
    try {
    if (null != out)
        out.close();
    } catch (IOException e) {
    LOG.error("Could not close output stream to hdfs", e);
    }
}
}

第二个进程读取这些文件以供进一步处理。 创建文件时,首先创建文件,然后填充内容。此过程需要时间(几毫秒,但仍然),在此期间,第二个进程可能会在完全写入和关闭之前拾取文件。

请注意,HDFS不会在namenode中保留锁定信息 - 因此没有守护进程可以在访问之前检查文件是否被锁定。

我想知道解决此问题的最佳方法是什么。

以下是我的想法:

  1. 完全写入并关闭文件后,将文件复制到新文件夹,然后是第二个进程 将从这个新文件夹中读取。
  2. 完全写入并关闭第二个进程后,根据某些命名约定重命名文件 将根据此命名惯例阅读。
  3. 我有一种感觉,我正在努力解决一个众所周知的问题而且我错过了一些东西。这样的问题是否有最佳实践?

3 个答案:

答案 0 :(得分:3)

Apache commons有一些东西。只需touch该文件,错误就会告诉您它是否已被锁定。

import org.apache.commons.io.*

boolean fileAvail = false;

try {
    FileUtils.touch(fileName); //throws IOException if being used
    fileAvail = true;
} catch (IOException e) {
    fileAvail = false;
}

(也)尝试使用资源

在Java 7中,您可以在实现Closable的任何文件,套接字和数据库连接上使用此功能,只要try块的作用域结束就会自动关闭

 try (FSDataOutputStream out = getFileSystem().create(outFilePath))
 {
   //use out in here
 }
 //No finally required - catch is optional

...保存所有额外的代码

答案 1 :(得分:2)

您是在同一个(JVM)流程中讨论two separate processes here or about two separate threads吗?

两种方式,这是一个consumer-producer problem,你在生产者和消费者之间缺少的是some proper synchronization。如果您在同一个JVM进程中运行两个线程,则可以使用BlockingQueue将某种文件传输完成的令牌从生产者传输到消费者,例如例如,文件完全写入并关闭其流后的文件名称。一旦在队列中找到文件名,消费者就可以确定该文件已完全写入并关闭,因为这是由生产者确认的。

但是,如果您使用两个不同的进程,问题有点难以解决,具体取决于其他组件的语言和网络设置,但您必须实现某些可供两者使用的队列例如,通过本地网络端口发送一些信息,以便进程知道彼此的工作。

无论如何,我总是避免在文件系统上移动文件,因为与发送简单令牌相比,这是一个相当昂贵的操作。此外,移动arround文件可能会暴露尚未完全移动的文件,具体取决于您使用的语言。

答案 2 :(得分:0)

你真的需要两个进程吗?为什么不创建两个线程然后加入它。