1

私の問題は次のようなものです:

クライアント側の HTTP キャッシュを作成しましたが、何らかの方法で HTTP ペイロードをファイル システムに保存する必要があります。不要なファイルでファイルシステムを乱雑にしたくありません。

私はこのクラスを書きました:

/*
 * 著作権 (c) 2008、コードハウス。全著作権所有。
 *
 * Apache ライセンス、バージョン 2.0 (「ライセンス」) に基づいてライセンス供与されています。
 * ライセンスに準拠する場合を除き、このファイルを使用することはできません。
 * ライセンスのコピーは、次の URL で入手できます。
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * 適用法で義務付けられている場合、または書面で合意されている場合を除き、ソフトウェア
 * ライセンスに基づいて配布されるものは、「現状のまま」で配布されます。
 * 明示または黙示を問わず、いかなる種類の保証または条件もありません。
 *許可を管理する特定の言語については、ライセンスを参照してください。
 * ライセンスに基づく制限。
 *
 */

パッケージ org.codehaus.httpcache4j.cache;

org.apache.commons.lang.Validate をインポートします。
org.apache.commons.io.filefilter.AndFileFilter をインポートします。
org.apache.commons.io.filefilter.DirectoryFileFilter をインポートします。
org.apache.commons.io.filefilter.RegexFileFilter をインポートします。

org.codehaus.httpcache4j.u​​til.DeletingFileFilter をインポートします。

java.io.File をインポートします。
java.io.FileFilter をインポートします。
java.io.Serializable をインポートします。
import java.util.ArrayList;
java.util.Arrays をインポートします。
java.util.Collections をインポートします。
java.util.List をインポートします。

/**
 * このクラスは内部的なものであり、クライアントによって使用されるべきではありません。
 *
 * ファイル世代の「プール」の作成と維持を担当します。
* ファイルはアクセスされるとプロモートされるため、どのファイルを削除してもよいかを判断できます。
* 既知の問題: これは、ストレージ エンジンのサイズと同期している必要があります。
* キャッシュに多くのアイテムがあるときに世代が少なすぎると、 * アクセスしようとすると、一部のファイルが欠落することがあります。 * * Despot からのメモ: ファイルを保存する別の方法を検討しているため、このクラスはいずれ廃止される可能性があります。 * または別のフォームに変更します。 * */ クラスFileGenerationManagerはSerializableを実装します{ private static final long serialVersionUID = -1558644426181861334L; プライベート最終ファイル baseDirectory; プライベートの最終的な int generationSize; プライベート最終int numberOfGenerations; プライベート最終ファイルフィルター生成フィルター; public FileGenerationManager(final File baseDirectory, final int numberOfGenerations) { this (baseDirectory, numberOfGenerations, 100); } public FileGenerationManager(final File baseDirectory, final int numberOfGenerations, final int generationSize) { Validate.isTrue(numberOfGenerations > 0, "0 世代は作成できません"); Validate.notNull(baseDirectory, "null のベース ディレクトリがない可能性があります"); if (!baseDirectory.exists()) { Validate.isTrue(baseDirectory.mkdirs(), "ベース ディレクトリを作成できませんでした: " + baseDirectory); } this.baseDirectory = baseDirectory; this.generationSize = generationSize; this.numberOfGenerations = numberOfGenerations; GenerationFilter = new AndFileFilter(DirectoryFileFilter.DIRECTORY, new RegexFileFilter("[0-9]*")); getGenerations(); } /** * ベースディレクトリにディレクトリの世代を作成します。 * * @作成された世代を返します。 */ //TODO: これは重いですか? //TODO: getFile() で失敗したときにこれを行うべきでしょうか? 公開同期リスト getGenerations() { 最終的なリストの世代 = new ArrayList(); //既存の世代を処理... File[] ディレクトリ = baseDirectory.listFiles(generationFilter); if (directories.length > 0) { for (ファイルディレクトリ: ディレクトリ) { generations.add(new Generation(baseDirectory, Integer.parseInt(directory.getName()))); } } そうしないと { generations.add(new Generation(baseDirectory, 1)); } Collections.sort(世代); 世代 currentGeneration = generations.get(0); if (currentGeneration.getGenerationDirectory().list().length > generationSize) { generations.add(0, new Generation(baseDirectory, currentGeneration.getSequence() + 1)); removeLastGeneration(世代); } while (generations.size() > numberOfGenerations) { removeLastGeneration(世代); } Collections.unmodifiableList(世代) を返します。 } private void removeLastGeneration(世代のリスト) { if (世代数.サイズ() > 世代数) { 世代世代 = generations.remove(generations.size() - 1); Generation.delete(); } } /** * 作成された最新の世代を返します * * @return シーケンス番号が最大の世代 */ 同期世代 getCurrentGeneration() { getGenerations().get(0); を返します。 } 公開同期ファイル getFile(String fileName) { ファイル ターゲット = new File(getCurrentGeneration().getGenerationDirectory(), fileName); for (世代世代: getGenerations()) { ファイル候補 = new File(generation.getGenerationDirectory(), fileName); if (candidate.exists()) { if (!target.equals(candidate)) { //なぜなら; http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4017593 target.delete(); if (!candidate.renameTo(target)) { 候補を返します。 } そうしないと { 壊す; } } } } ターゲットを返します。 } static class Generation は Comparable を実装します { プライベート ファイル生成ディレクトリ。 プライベート int シーケンス。 public Generation(final File baseDir, final int generationNumber) { Validate.notNull(baseDir, "生成ディレクトリは null ではない可能性があります"); ファイル genFile = new File(baseDir, String.valueOf(generationNumber)); genFile.mkdirs(); this.generationDirectory = genFile; this.sequence = 世代番号; } パブリック同期ボイド削除(){ File[] undeleteableFiles = generationDirectory.listFiles(新しい DeletingFileFilter()); if (undeleteableFiles == null || undeleteableFiles.length == 0) { GenerationDirectory.delete(); } そうしないと { System.err.println("これらのファイルを削除できません: " + Arrays.toString(undeleteableFiles)); } } public File getGenerationDirectory() { GenerationDirectory を返します。 } public int getSequence() { 戻りシーケンス; } public int compareTo(世代世代) { return 1 - (シーケンス - generation.sequence); } } }

問題は、ファイルが正しいフォルダーに移動されないことがあり、ファイル記述子がリークする可能性があることです。

これを改善する方法について何か提案はありますか?

おそらくこれに対する標準的な解決策はありますか?言語に関係なく?

これもかなり遅いので、速度の向上は大歓迎です。

4

1 に答える 1

3

パフォーマンスの問題 (およびおそらくバグ) は、この情報をメモリに保存するのではなく、世代をマークする際にファイル システムを過度に使用することが原因である可能性があります。ファイル システム アクセスは、メモリ アクセスよりもはるかにコストがかかります。特に、File.listFiles() または File.list() は非常に遅くなる可能性があります。数千のファイルがある場合、 NTFS を使用する Windows システムでの実行には数ミリ秒ではなく数秒かかる と予想してください。

可能であれば、すべての世代情報は、同期されたコレクション内のオブジェクトとして保存および更新する必要があります。キャッシュされたデータ ファイルを実際に保存、取得、および削除するためにファイル システムを使用するだけの場合は、すべてのキャッシュ ファイルを 1 つのディレクトリに貼り付けて、任意の名前を付けることができます (ファイルに番号またはランダムな名前を付けるだけです)。

世代キャッシュ情報を永続化し、アプリケーションの突然のシャットダウンに対して安全である必要がある場合は、シリアル化されたコレクションを使用して、これを定期的にディスクに書き込むことができます (たとえば、30 秒ごと、アプリケーションのシャットダウン時に再び)。単なるキャッシュなので、アプリの起動時にチェックを行い、実際のファイルなしでキャッシュ エントリを削除したり、キャッシュ エントリなしでファイルを削除したりできます。

または、埋め込みデータベースを使用してキャッシュ全体を保存することを検討することもできます。H2 または HSQLDB は純粋な Java であり、非常に高速で軽量であり、メモリ内データベースとさらに高速な組み込みモードをサポートしています。これにより、より多くのキャッシュ オブジェクトを格納できるようになり、DBMS が頻繁に使用される項目を RAM にキャッシュする可能性があるため、処理速度が大幅に向上する可能性があります。

于 2009-04-27T22:20:54.277 に答える