2009-04-27 9 views
1

Mon problème est comme ceci:fichiers dans « Organiser des seaux »

J'ai un écrit un cache HTTP côté client et je dois stocker la charge utile HTTP dans le système de fichiers en quelque sorte. Je ne veux pas encombrer le système de fichiers avec des fichiers inutiles.

J'ai écrit cette classe:

 

/* 
* Copyright (c) 2008, The Codehaus. All Rights Reserved. 
* 
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
* 
*/ 

package org.codehaus.httpcache4j.cache; 

import org.apache.commons.lang.Validate; 
import org.apache.commons.io.filefilter.AndFileFilter; 
import org.apache.commons.io.filefilter.DirectoryFileFilter; 
import org.apache.commons.io.filefilter.RegexFileFilter; 

import org.codehaus.httpcache4j.util.DeletingFileFilter; 

import java.io.File; 
import java.io.FileFilter; 
import java.io.Serializable; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Collections; 
import java.util.List; 

/** 
* This class is internal and should never be used by clients. 
* 
* Responsible for creating and maintaining a "Pool" of file generations. 
* The files are promoted when they are accessed, so we can figure out which files that are OK to delete.
* Known Gotchas: This needs to be in sync with the size of the storage engine.
* If you have too few generations when you have many items in the cache, you might * be missing some files when you try to access them. * * Note from Despot: I am looking into another way of storing files, so this class might go away at some point, * or change to a different form. * */ class FileGenerationManager implements Serializable{ private static final long serialVersionUID = -1558644426181861334L; private final File baseDirectory; private final int generationSize; private final int numberOfGenerations; private final FileFilter generationFilter; 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, "You may not create 0 generations"); Validate.notNull(baseDirectory, "You may not have a null base directory"); if (!baseDirectory.exists()) { Validate.isTrue(baseDirectory.mkdirs(), "Could not create base directory: " + baseDirectory); } this.baseDirectory = baseDirectory; this.generationSize = generationSize; this.numberOfGenerations = numberOfGenerations; generationFilter = new AndFileFilter(DirectoryFileFilter.DIRECTORY, new RegexFileFilter("[0-9]*")); getGenerations(); } /** * Creates generations of the directories in the base directory. * * @return the created generations. */ //TODO: Is this heavy? //TODO: Maybe we should do this when we miss in getFile() ? public synchronized List getGenerations() { final List generations = new ArrayList(); //handle existing generations... File[] directories = baseDirectory.listFiles(generationFilter); if (directories.length > 0) { for (File directory : directories) { generations.add(new Generation(baseDirectory, Integer.parseInt(directory.getName()))); } } else { generations.add(new Generation(baseDirectory, 1)); } Collections.sort(generations); Generation currentGeneration = generations.get(0); if (currentGeneration.getGenerationDirectory().list().length > generationSize) { generations.add(0, new Generation(baseDirectory, currentGeneration.getSequence() + 1)); removeLastGeneration(generations); } while (generations.size() > numberOfGenerations) { removeLastGeneration(generations); } return Collections.unmodifiableList(generations); } private void removeLastGeneration(List generations) { if (generations.size() > numberOfGenerations) { Generation generation = generations.remove(generations.size() - 1); generation.delete(); } } /** * Returns the most recent created generation * * @return the generation with the highest sequence number */ synchronized Generation getCurrentGeneration() { return getGenerations().get(0); } public synchronized File getFile(String fileName) { File target = new File(getCurrentGeneration().getGenerationDirectory(), fileName); for (Generation generation : getGenerations()) { File candidate = new File(generation.getGenerationDirectory(), fileName); if (candidate.exists()) { if (!target.equals(candidate)) { //because of; http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4017593 target.delete(); if (!candidate.renameTo(target)) { return candidate; } else { break; } } } } return target; } static class Generation implements Comparable { private File generationDirectory; private int sequence; public Generation(final File baseDir, final int generationNumber) { Validate.notNull(baseDir, "Generation directory may not be null"); File genFile = new File(baseDir, String.valueOf(generationNumber)); genFile.mkdirs(); this.generationDirectory = genFile; this.sequence = generationNumber; } public synchronized void delete() { File[] undeleteableFiles = generationDirectory.listFiles(new DeletingFileFilter()); if (undeleteableFiles == null || undeleteableFiles.length == 0) { generationDirectory.delete(); } else { System.err.println("Unable to delete these files: " + Arrays.toString(undeleteableFiles)); } } public File getGenerationDirectory() { return generationDirectory; } public int getSequence() { return sequence; } public int compareTo(Generation generation) { return 1 - (sequence - generation.sequence); } } }

Le problème est que, parfois, les fichiers ne sont pas déplacés dans le dossier correct, et je pourrais fuir les descripteurs de fichiers.

Avez-vous des suggestions pour améliorer cela?

Existe-t-il une solution standard pour cela? quelle que soit la langue?

Ceci est également assez lent, les améliorations de vitesse sont les bienvenues.

+0

Que voulez-vous dire "parfois les fichiers ne sont pas déplacés vers le bon dossier"? est-ce un bug dans votre code ou avez-vous une erreur? Aussi où est-ce lent? Il est impossible de dire où le temps est passé en regardant le code. –

+0

Sur Windows, il semble que parfois le fichier cible est verrouillé, car c'est le seul système d'exploitation qui échoue réellement. Si c'est un bug dans mon code, je ne l'ai pas vu. Le temps est passé dans la méthode File.listFiles(). Comme un certain nombre d'objets sont créés dans cette méthode. – ngarthl

Répondre

3

Votre problème de performances (et probablement des bogues) est probablement dû à l'utilisation excessive du système de fichiers pour marquer les générations, plutôt que de stocker ces informations en mémoire. L'accès au système de fichiers est beaucoup plus cher que l'accès à la mémoire - en particulier, File.listFiles() ou File.list() peut être très lent. Si vous avez plusieurs milliers de fichiers, attendez-vous à prendre secondes plutôt que des millisecondes pour exécuter sur un système Windows NTFS.

Si possible, toutes les informations générationnelles doivent être stockées et mises à jour en tant qu'objets dans une collection synchronisée. Si vous utilisez simplement le système de fichiers pour stocker, récupérer et supprimer les fichiers de données mis en cache, vous pouvez coller tous les fichiers cache dans un répertoire et les appeler comme vous le souhaitez (donnez simplement un nom au fichier ou un nom aléatoire). Si les informations de cache générationnelles doivent être persistantes et sécurisées contre une fermeture brutale des applications, vous pouvez utiliser des collections sérialisées et les écrire périodiquement sur le disque (par exemple, toutes les 30 secondes, et à nouveau à l'arrêt de l'application). Puisque c'est juste un cache, vous pouvez faire une vérification sur le démarrage de l'application et supprimer les entrées de cache avec un vrai fichier et supprimer des fichiers sans une entrée de cache.

Alternativement, vous pourriez envisager d'utiliser une base de données intégrée pour stocker le cache entier. H2 ou HSQLDB sont de purs Java, très rapides et légers, et supportent les bases de données en mémoire et les modes embarqués qui sont encore plus rapides. Cela vous permettrait de stocker beaucoup plus d'objets de cache, et pourrait être beaucoup plus rapide, puisque le SGBD peut mettre en mémoire cache les éléments fréquemment utilisés dans la RAM.

Questions connexes