我有一些Java代码可以将XML文件输出到NFS挂载的文件系统。在另一台将filesytem挂载为Samba共享的服务器上,有一个进程正在运行,每30秒轮询一次新的XML文件。如果找到新文件,则会对其进行处理,然后重命名为备份文件。 99%的时间,文件都没有问题。但是,备份文件时不时会包含部分写入的文件。
在与其他人讨论之后,我们猜测外部服务器上运行的进程在读取文件时会干扰Java输出流。他们建议首先创建一个.temp类型的文件,然后在文件写入完成后将其重命名为.xml。一种常见的行业惯例。更改后,重命名每次都失败。
有些研究表明,使用NFS挂载的文件系统时,Java文件I / O是错误的。
帮助我Java大师!我该如何解决这个问题?
以下是一些相关信息:
以下是我的代码示例:
//Write the file
XMLOutputter serializer = new XMLOutputter(Format.getPrettyFormat());
FileOutputStream os = new FileOutputStream(outputDirectory + fileName + ".temp");
serializer.output(doc, os);//doc is a constructed xml document using JDOM
os.flush();
os.close();
//Rename the file
File oldFile = new File(outputDirectory + fileName + ".temp");
File newFile = new File(fileName + ".xml");
boolean success = oldFile.renameTo(newFile);
if (!success) {
// File was not successfully renamed.
throw new IOException("The file " + fileName + ".temp could not be renamed.");
}//if
答案 0 :(得分:2)
您可能必须在新文件名中指定完整路径:
File newFile = new File(outputDirectory + fileName + ".xml");
答案 1 :(得分:2)
对我来说这看起来像个错误:
File oldFile = new File(outputDirectory + fileName + ".temp");
File newFile = new File(fileName + ".xml");
我原本预料到了这一点:
File oldFile = new File(outputDirectory + fileName + ".temp");
File newFile = new File(outputDirectory + fileName + ".xml");
通常,听起来XML文件的写入与读取/处理/重命名任务之间存在竞争条件。您是否可以让读取/处理/重命名任务仅对文件进行操作> 1分钟或类似的东西?
或者,让Java程序在完成写出XML文件后写出一个额外的空文件,该文件表明已完成对XML文件的写入。仅在信号文件存在时读取/处理/重命名XML文件。然后删除信号文件。
答案 2 :(得分:2)
原始错误肯定听起来像是对文件进行并发访问的问题 - 您的解决方案应该有效,但也有其他解决方案。
例如,在自动读取过程中放置一个计时器,以便在检测到新文件时记录文件大小,睡眠X秒,然后如果大小不匹配则重新启动计时器。这应该避免部分文件传输的问题。
编辑:或者检查上面的时间戳以检查这一点,但要确保它足够老,时间戳中的任何不精确都无关紧要(例如,自上次修改后10秒到1分钟)。
或者,试试这个:
File f = new File("foo.xml");
FileOutputStream fos = new FileOutputStream(f);
FileChannel fc = fos.getChannel();
FileLock lock = fc.lock();
(DO FILE WRITE)
fis.flush();
lock.release();
fos.close();
这应该使用本机操作系统文件锁定来阻止其他程序(例如XML读取器守护程序)的并发访问。
就NFS故障而言:有一个记录在案的“功能”(bug),文件无法通过Java中的“重命名”在文件系统之间移动。可能有混淆,因为它在NFS文件系统上?
答案 3 :(得分:1)
一般来说,NFS的一些信息。根据您的NFS设置,锁可能根本不起作用,并且许多大型NFS安装都会针对读取性能进行调整,因此,由于缓存效应,新数据可能会比预期更晚出现。
我已经看到了你创建文件,添加数据的效果(这是在另一台机器上看到的),但之后的所有数据都出现了30秒的延迟。
顺便说一句,最佳解决方案是旋转文件架构。因此,假定最后一个被写入,之前的那个被安全地写入并且可以被读取。我不会在单个文件上工作并将其用作“管道”。
您也可以使用在写入并正确关闭大文件后写入的空文件。因此,如果小家伙在那里,那个大家伙肯定会完成并且可以阅读。
答案 4 :(得分:0)
可能是由于“重命名操作可能无法将文件从一个文件系统移动到另一个文件系统”http://java.sun.com/j2se/1.5.0/docs/api/java/io/File.html#renameTo%28java.io.File%2) 尝试使用apache commons io FiltUtils.copyFileToDirectory http://commons.apache.org/io/api-release/org/apache/commons/io/FileUtils.html#copyFileToDirectory(java.io.File,%20java.io.File)代替