正在从批处理应用程序中的Files.move获取java.nio.file.NoSuchFileException



背景

这是一个多线程的批处理应用程序,每个线程都有自己的文件。我在其他地方有逻辑,可以在文件创建失败的情况下阻止文件重命名。

这个进程作为守护进程运行,每天生成几千个文件。这种异常可能每3天发生1个文件,因此我们使用的方法在大多数情况下都有效。

运行批次的机器是Red Hat Enterprise Linux Server release 6.7 (Santiago)

Java版本为1.8.0_162

临时文件名是通过附加java.util.UUID.中UUID.randomUUID((的结果生成的

真正的文件名可能重复,这就是为什么我们使用rand UUID而不是.tmp作为临时文件名。这不应该是一个问题,因为移动部分在一个同步块中。

异常

2018-07-26 15:06:01,743 ERROR (ProcessRecordsTask.java:renameFileAfterProcess():674)  - Error: Unable to rename file:
java.nio.file.NoSuchFileException: /logs/apps/appname/FILNAMESTUFF_07_26_2018_15_05_51.xml.5c80331c-3b7e-4e16-90d7-c0d7810451c5 -> /logs/apps/appname/FILNAMESTUFF_07_26_2018_15_05_51.xml
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixCopyFile.move(UnixCopyFile.java:396)
at sun.nio.fs.UnixFileSystemProvider.move(UnixFileSystemProvider.java:262)
at java.nio.file.Files.move(Files.java:1395)
at com.filetransferbatch.task.ProcessRecordsTask.renameFileAfterProcess(ProcessRecordsTask.java:664)
at com.filetransferbatch.task.ProcessRecordsTask.saveFileData(ProcessRecordsTask.java:349)
at com.filetransferbatch.task.ProcessRecordsTask.xmlTransfer(ProcessRecordsTask.java:244)
at com.filetransferbatch.task.ProcessRecordsTask.call(ProcessRecordsTask.java:162)
at com.filetransferbatch.task.ProcessRecordsTask.call(ProcessRecordsTask.java:62)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

我从以下片段中得到了异常:

private boolean renameFileAfterProcess(String tmpFileName) {
boolean fileRenamed = false;
try {
if (null != tmpFileName && (!("".equals(tmpFileName)))) {
Path tmpFilePath = Paths.get(tmpFileName);
logger.info("tmpFilePath:" + tmpFilePath + ":Renamed Filepath: " + realFilePath);
Path realFile = Paths.get(realFilePath);
synchronized (this) {
logger.info("File " + tmpFilePath + " exists: " + Files.exists(tmpFilePath));
Files.move( tmpFilePath,
realFile,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.ATOMIC_MOVE);
logger.info(tmpFileName + ":File was successfully renamed to :" + realFilePath);
fileRenamed = true;
}
}
} catch (IOException e) {
fileRenamed = false;
logger.error("Error: Unable to rename file:", e);
} catch (Exception e) {
logger.error("Error :", e);
}
return fileRenamed;
}

文件是以这种方式创建的

private boolean createFile(String fileName, byte[] fileDataMerged) {
boolean fileCreated = false;
if (fileName.trim().length() != 0) {
try {
Path createdFilePath = Files.write( Paths.get(tmpFilePath),
fileDataMerged,
StandardOpenOption.SYNC,
StandardOpenOption.CREATE,
StandardOpenOption.WRITE);
if (createdFilePath != null) {
fileCreated = Files.exists(createdFilePath);
}
} catch (IOException e) {
logger.error("Error writing temp file: ", e);
} catch (Exception e) {
logger.error("Error writing temp file: ", e);
}
}
return fileCreated;
}

我唯一能想到的解决办法就是让线程休眠几毫秒,以防这是一个文件系统级别的问题。问题是,在非生产环境中复制异常确实很困难。

我怀疑,当几乎所有线程都有相同的真实文件名时,就会发生异常,所以会有一堆重命名为相同的文件名,但我不能确定这一点。

感谢

**编辑:**

我们运行了一个skybot作业,它抓取扩展名为.csv的文件,这些文件比一天还旧。我认为作业在寻找要移动的文件时锁定了文件夹中的所有文件。在我进行了代码修复,允许我删除skybot作业后,问题就消失了。

Files.move之前运行Files.createDirectories,如示例所示:

Files.createDirectories(Paths.get("pathTo"));
Files.move(Paths.get("pathFrom"), Paths.get("pathTo"));

相关内容

  • 没有找到相关文章

最新更新