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

我有两个进程在运行。 一种是将文件写入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. 完全写入并关闭后,根据某些命名约定重命名文件,然后第二个进程将根据此命名约定进行读取。

我有一种感觉,我正在努力解决一个众所周知的问题而且我错过了一些东西。 这样的问题有最好的做法吗?

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; } 

(也)尝试使用Resources

在Java 7中,您可以在实现Closable任何内容上使用此function,例如文件,套接字和数据库连接,只要try块的范围结束,它就会自动关闭

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

…保存所有额外的代码

您是在谈论这里的两个独立进程还是在同一个(JVM)进程中的两个独立线程 ?

两种方式,这都是消费者 – 生产者问题,而您缺少的是生产者和消费者之间的一些适当的同步 。 如果您在同一个JVM进程中运行两个线程,则可以使用BlockingQueue将某种文件传输完成的令牌从生产者传输到使用者,例如文件完全写入后的文件名称。它的溪流关闭了。 一旦在队列中找到文件名,消费者就可以确定该文件已完全写入并关闭,因为这是由生产者确认的。

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

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

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