我有一个用java.io. filewriter写文件的java程序。在生产环境中,这个程序被实例化几次,每个实例在NFS中的同一目录上写一个唯一的文件。有时在同一时间有这么多的实例,其中一些(只是少数)是失败的IOException和消息读"错误:权限被拒绝"。
我认为这里的问题是目录上的并发性,我想通过添加重试逻辑来解决它,它必须写文件,无论如何。
我想到的是如下所示,但我不知道它是否会起作用,因为我没有办法在我的开发环境中进行测试:
try {
fw.write(str);
} catch (IOException e) {
boolean retry = true;
while(retry) {
TimeUnit.MILLISECONDS.sleep(2);
fw.write(str);
retry = false;
}
} finally {
fw.close();
}
我很感激你能给我的任何帮助。
有时在同一时间有如此多的实例,其中一些(只是少数)使用IOException失败,消息读取"错误:权限被拒绝"。
我想通过添加一个重试逻辑来解决它,它必须写文件,无论什么
你这是自找麻烦。
你的理论(这是相当合理的)是,在底层操作系统和/或文件系统驱动程序中存在一些机制,允许你创建新的文件句柄,但如果并发的句柄太多,最终会抛出错误。
这意味着你的系统有时"发送的数据超过了系统的处理能力",你提出的解决方案包括每2毫秒重复尝试这些写入,直到它们成功。如果系统的发送量超过底层系统的处理能力,这意味着你有大量的"生产者"每隔200毫秒并发地重新发送一次,并且考虑到"接收系统"(操作系统/文件系统驱动程序)显然会不堪重负,你……如果有100个线程,每个线程每隔2毫秒触发一个写请求,那就没有帮助了。
一个更好的系统是采用队列机制:不要让100个线程都重复地向OS/文件系统发送写请求,每隔2毫秒重试一次,你应该有一个队列,让一个线程或一个硬限制的线程池来尝试。
阅读java.util.concurrent和朋友们关于如何创建一个处理作业的池+一种提供作业并等待它被处理的方法。
或者,如果你真的想沿着这条路走下去,并诅咒后果,你有四个问题与粘贴的代码:
重试不受保护
catch块不适用于自身。它内部发生的任何异常,只是发生,它们不会被"捕获"。将其全部重写为while循环;catch块本身不应该触发write
,它应该导致循环再次运行。
无指数回退
如果你重试@ 2毫升,它没有工作几次,这是一个好主意,开始等待比2毫升稍长的时间。你应该等待的时间越来越长,这也有助于引入随机因素。(见指数回退)
无限制计数器
最终,在多次尝试之后,你的应用程序最好放弃并崩溃(根本没有发生写),而不是永远挂起。
不创建FileWriter
一旦fw.write
开始抛出异常,它很可能会永远继续这样做,即使底层OS/fs现在有时间/空间来处理你的写请求。最好每次都重新创建整个东西。
把它们放在一起
int count = 0;
while (true) {
try {
try (FileWriter fw = ...) {
fw.write(str);
}
break; // break out of the while.
} catch (IOException e) {
// you may want to test if that IOException indeed
// indicates a problem that 'retry' can fix.
// because if it isn't, this code runs a very very
// long time before the 1000 hard-limit kicks in!
// after 1000 retries, give up.
if (count++ >= 1000) throw e;
TimeUnit.MILLISECONDS.sleep(rnd.nextInt(count * 4));
}
}