2

XMLファイルをNFSマウントされたファイルシステムに出力するJavaコードが少しあります。ファイルシステムがSamba共有としてマウントされている別のサーバーでは、30秒ごとに新しいXMLファイルをポーリングするプロセスが実行されています。新しいファイルが見つかった場合、そのファイルは処理され、バックアップファイルとして名前が変更されます。99%の確率で、ファイルは問題なく書き込まれます。ただし、バックアップファイルに部分的に書き込まれたファイルが含まれる場合があります。

他の人と話し合った結果、外部サーバーで実行されているプロセスが、ファイルの読み取り時にJava出力ストリームに干渉していると推測しました。彼らは、最初にタイプ.tempのファイルを作成し、ファイルの書き込みが完了した後に.xmlに名前を変更することを提案しました。一般的な業界慣行。変更後、名前の変更は毎回失敗します。

一部の調査では、NFSマウントされたファイルシステムで作業する場合、JavaファイルI/Oにバグがあることが判明しました。

Javaの達人を助けてください!この問題を解決するにはどうすればよいですか?

関連する情報は次のとおりです。

  • 私のプロセスは、Solaris10で実行されているJava1.6.0_16です。
  • マウントされたファイルシステムはNASです
  • ポーリングプロセスを備えたサーバーは、Windows Server 2003 R2 Standard、ServicePack2です。

これが私のコードのサンプルです:

//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
4

5 に答える 5

2

おそらく、新しいファイル名に完全なパスを指定する必要があります。

File newFile = new File(outputDirectory + fileName + ".xml");
于 2009-11-24T16:29:42.987 に答える
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 分以上前のファイルまたは類似のものに対してのみ実行することはできますか?

または、XML ファイルへの書き込みが完了したことを通知する XML ファイルの書き込みが完了したら、Java プログラムに追加の空のファイルを書き出させます。シグナル ファイルが存在する場合にのみ、XML ファイルの読み取り/処理/名前変更を行います。次に、シグナル ファイルを削除します。

于 2009-11-24T16:30:51.097 に答える
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();

これは、ネイティブ OS ファイル ロックを使用して、他のプログラム (XML リーダー デーモンなど) による同時アクセスを防止する必要があります。

NFS の不具合に関する限り: Java の「名前変更」を介してファイルシステム間でファイルを移動できないという文書化された「機能」(バグ) があります。NFS ファイルシステム上にあるため、混乱する可能性はありますか?

于 2009-11-24T16:38:19.770 に答える
1

一般的な NFS に関する情報。NFS 設定によっては、ロックがまったく機能しない場合があり、多くの大規模な NFS インストールは読み取りパフォーマンス用に調整されているため、キャッシュの影響により、新しいデータが予想よりも遅く表示される場合があります。

ファイルを作成し、データを追加した結果 (これは別のマシンで見られました) を見たことがありますが、その後のすべてのデータは 30 秒の遅延で表示されました。

ところで、最善の解決策は、ローテーション ファイル スキーマです。そのため、最後のものは書き込まれたと見なされ、前のものは安全に書き込まれ、読み取ることができます。私は単一のファイルで作業せず、それを「パイプ」として使用します。

代わりに、大きなファイルが書き込まれて適切に閉じられた後に書き込まれた空のファイルを使用することもできます。したがって、小さな人がそこにいる場合、大きな人は間違いなく完了しており、読むことができます.

于 2009-11-24T17:20:52.913 に答える
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)代わりに

于 2009-11-24T16:49:37.173 に答える