1

我的问题是这样的:

我编写了一个客户端 HTTP 缓存,我需要以某种方式将 HTTP 有效负载存储在文件系统中。我不想用不必要的文件弄乱文件系统。

我写了这个类:

/*
 * 版权所有 (c) 2008,The Codehaus。版权所有。
 *
 * 根据 Apache 许可证 2.0 版(“许可证”)获得许可;
 * 除非遵守许可,否则您不得使用此文件。
 * 您可以在以下网址获取许可证的副本
 * 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;
导入 java.util.ArrayList;
导入 java.util.Arrays;
导入 java.util.Collections;
导入 java.util.List;

/**
 * 这个类是内部的,不应该被客户端使用。
 *
 * 负责创建和维护文件生成的“池”。
* 文件在被访问时被提升,因此我们可以确定哪些文件可以删除。
* 已知问题:这需要与存储引擎的大小同步。
* 如果缓存中有很多项目时生成的代数太少,则可能 * 当您尝试访问它们时会丢失一些文件。 * * Despot 的注释:我正在研究另一种存储文件的方式,所以这个类可能会在某个时候消失, * 或更改为其他形式。 * */ 类 FileGenerationManager 实现 Serializable{ 私有静态最终长序列版本UID = -1558644426181861334L; 私有最终文件基目录; 私人最终 int generationSize; 私人最终 int numberOfGenerations; private final FileFilter generationFilter; 公共 FileGenerationManager(最终文件 baseDirectory,最终 int numberOfGenerations){ 这(baseDirectory,numberOfGenerations,100); } 公共 FileGenerationManager(最终文件 baseDirectory,最终 int numberOfGenerations,最终 int generationSize){ Validate.isTrue(numberOfGenerations > 0, "你不能创建 0 代"); Validate.notNull(baseDirectory, "你可能没有一个空的基本目录"); 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(); } /** * 在基本目录中创建几代目录。 * * @return 创建的世代。 */ //TODO: 这个重吗? //TODO: 当我们错过 getFile() 时,也许我们应该这样做? 公共同步列表 getGenerations() { 最终列表世代 = new ArrayList(); //处理现有的世代... 文件[] 目录 = baseDirectory.listFiles(generationFilter); 如果(目录.长度> 0){ 对于(文件目录:目录){ generation.add(new Generation(baseDirectory, Integer.parseInt(directory.getName()))); } } 别的 { generation.add(new Generation(baseDirectory, 1)); } Collections.sort(世代); 一代 currentGeneration = generation.get(0); if (currentGeneration.getGenerationDirectory().list().length > generationSize) { generation.add(0, new Generation(baseDirectory, currentGeneration.getSequence() + 1)); removeLastGeneration(世代); } 而 (generations.size() > numberOfGenerations) { removeLastGeneration(世代); } 返回 Collections.unmodifiableList(generations); } 私人无效removeLastGeneration(列表世代){ if (generations.size() > numberOfGenerations) { 一代一代=generations.remove(generations.size() - 1); 代.删除(); } } /** * 返回最近创建的一代 * * @return 具有最高序列号的世代 */ 同步生成 getCurrentGeneration() { 返回 getGenerations().get(0); } 公共同步文件getFile(字符串文件名){ 文件目标 = 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 目标.删除(); if (!candidate.renameTo(target)) { 返回候选人; } 别的 { 休息; } } } } 返回目标; } 静态类生成实现 Comparable { 私有文件生成目录; 私有 int 序列; public Generation(final File baseDir, final int generationNumber) { Validate.notNull(baseDir, "生成目录不能为空"); 文件 genFile = new File(baseDir, String.valueOf(generationNumber)); genFile.mkdirs(); this.generationDirectory = genFile; this.sequence = generationNumber; } 公共同步无效删除(){ File[] undeleteableFiles = generationDirectory.listFiles(new DeletingFileFilter()); if (undeleteableFiles == null || undeleteableFiles.length == 0) { generationDirectory.delete(); } 别的 { System.err.println("无法删除这些文件:" + Arrays.toString(undeleteableFiles)); } } 公共文件getGenerationDirectory(){ 返回生成目录; } 公共 int getSequence() { 返回序列; } public int compareTo(一代){ return 1 - (sequence - generation.sequence); } } }

问题是有时文件没有移动到正确的文件夹,我可能会泄漏文件描述符。

您对如何改善这一点有什么建议吗?

这可能有标准解决方案吗?不分语言?

这也很慢,欢迎提高速度。

4

1 回答 1

3

您的性能问题(可能还有错误)可能是由于在标记世代时过度使用文件系统,而不是将此信息存储在内存中造成的。文件系统访问比内存访问要昂贵得多——特别是File.listFiles() 或 File.list() 可能非常慢。 如果您有数千个文件,预计在使用 NTFS 的 Windows 系统上执行它需要几秒钟而不是几毫秒。

如果可能,应将所有世代信息作为对象存储和更新为同步集合中的对象。如果您只是使用文件系统来实际存储、检索和删除缓存的数据文件,您可以将所有缓存文件放在一个目录中,然后随意调用它们(只需给文件一个数字或随机名称)。

如果分代缓存信息需要持久且安全地防止应用程序突然关闭,您可以使用序列化集合并定期将其写入磁盘(例如,每 30 秒一次,并在应用程序关闭时再次写入)。由于它只是一个缓存,因此您可以检查应用程序启动并删除没有真实文件的缓存条目并删除没有缓存条目的文件。

或者,您可能会考虑使用嵌入式数据库来存储整个缓存。H2 或 HSQLDB 是纯 Java,非常快速和轻量级,并且支持内存数据库和更快的嵌入式模式。这将使您可以存储更多的缓存对象,并且可能会更快,因为 DBMS 可能会在 RAM 中缓存经常使用的项目。

于 2009-04-27T22:20:54.277 回答