题 ehcache坚持磁盘问题


我想用Java中的ehcache做一些我认为应该非常简单的事情,但是我花了足够的时间来挫败自己的文档......

  1. 将值写入磁盘永久缓存。关掉。

  2. 再次启动并读取该值。

这是我的Java函数:

private static void testCacheWrite() {

  // create the cache manager from our configuration
  URL url = TestBed.class.getClass().getResource("/resource/ehcache.xml");
  CacheManager manager = CacheManager.create(url);
  // check to see if our cache exits, if it doesn't create it
  Cache testCache = null;
  if (!manager.cacheExists("test")) {
    System.out.println("No cache found. Creating cache...");
    int maxElements = 50000;
    testCache = new Cache("test", maxElements,
      MemoryStoreEvictionPolicy.LFU, true, null, true, 60, 30,
      true, Cache.DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS, null);
    manager.addCache(testCache);
    // add an element to persist
    Element el = new Element("key", "value");
    testCache.put(el);
    testCache.flush();
    System.out.println("Cache to disk. Cache size on disk: " +
      testCache.getDiskStoreSize());
  } else {
    // cache exists so load it
    testCache = manager.getCache("test");
    Element el = testCache.get("key");
    if (null == el) {
      System.out.print("Value was null");
      return;
    }
    String value = (String) el.getObjectValue();
    System.out.println("Value is: " + value);
  }
  manager.shutdown();
}

这是我的缓存配置(ehcache.xml):

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
  <diskStore path="C:/mycache"/><!-- java.io.tmpdir -->
  <defaultCache
    maxElementsInMemory="10000"
    eternal="true"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    overflowToDisk="true"
    maxElementsOnDisk="10000000"
    diskPersistent="true"
    diskExpiryThreadIntervalSeconds="120"
    memoryStoreEvictionPolicy="LRU" />
</ehcache>

即使我在第一次运行后在磁盘上看到test.index和test.data文件,此函数的输出始终如下(它似乎永远不会从磁盘加载缓存):

找不到缓存。创建缓存...
    缓存到磁盘。磁盘上的缓存大小:2

我必须在这里做一些蠢事,但我不确定是什么!


24
2017-11-13 14:37


起源




答案:


好吧,我所做的就是使用配置文件配置我的缓存。这是更新的配置:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <diskStore path="C:/mycache" />

    <defaultCache
        maxElementsInMemory="10000" 
        eternal="true"
        timeToIdleSeconds="120" 
        timeToLiveSeconds="120" 
        overflowToDisk="true"
        maxElementsOnDisk="10000000" 
        diskPersistent="true"
        diskExpiryThreadIntervalSeconds="120" 
        memoryStoreEvictionPolicy="LRU" />

    <cache 
        name="test" 
        maxElementsInMemory="500" 
        eternal="true"
        overflowToDisk="true" 
        timeToIdleSeconds="300" 
        timeToLiveSeconds="600"
        diskPersistent="true" 
        diskExpiryThreadIntervalSeconds="1"
        memoryStoreEvictionPolicy="LFU" />

</ehcache>

所以基本上我没有使用构造函数来定义缓存。

我想这会起作用,但我仍然想知道为什么编程定义的缓存不能保留在磁盘上(特别是因为它们仍然写入磁盘!)。

谢谢你们的评论。


16
2017-11-13 16:38



你能帮我解决这个问题。 stackoverflow.com/questions/45295279/... - ali


在用调试器花了一些时间后,我相信我有一个OP的答案。

问题(至少从我所看到的)围绕非集群磁盘缓存文件以及如何重新读回。在文件net.sf.ehcache.store.compound.factories.DiskPersistentStorageFactory.java中,方法:

public DiskPersistentStorageFactory(Ehcache cache, String diskPath) {
    super(getDataFile(diskPath, cache), cache.getCacheConfiguration().getDiskExpiryThreadIntervalSeconds(),
            cache.getCacheConfiguration().getDiskSpoolBufferSizeMB(), cache.getCacheEventNotificationService(), false);

    indexFile = new File(getDataFile().getParentFile(), getIndexFileName(cache));
    flushTask = new IndexWriteTask(indexFile, cache.getCacheConfiguration().isClearOnFlush());

    if (!getDataFile().exists() || (getDataFile().length() == 0)) {
        LOG.debug("Matching data file missing (or empty) for index file. Deleting index file " + indexFile);
        indexFile.delete();
    } else if (getDataFile().exists() && indexFile.exists()) {
        if (getDataFile().lastModified() > (indexFile.lastModified() + TimeUnit.SECONDS.toMillis(1))) {
            LOG.warn("The index for data file {} is out of date, probably due to an unclean shutdown. " 
                    + "Deleting index file {}", getDataFile(), indexFile);
            indexFile.delete();
        }
    }

    diskCapacity = cache.getCacheConfiguration().getMaxElementsOnDisk();
    memoryCapacity = cache.getCacheConfiguration().getMaxElementsInMemory();
    memoryPolicy = determineEvictionPolicy(cache.getCacheConfiguration());
}

检查数据文件的时间戳。我看到的问题是,无论我最终如何关闭缓存/管理器,文件永远不会正确同步。我快速而又肮脏的解决方法是将数据文件的时间调整为刚刚超过索引文件的时间戳:

File index = new File( path, name + ".index" );
File data  = new File( path, name + ".data"  );

data.setLastModified( index.lastModified() + 1 );

当然,这并不优雅,但它满足了我的需求,因为我们的项目使用集群缓存,这允许我使用持久缓存独立调试......而无需在本地运行Terracotta。

需要注意的是,对于非群集缓存,我必须在每次put()和remove()之后刷新()以保持磁盘映像新鲜,特别是在调试时由于缺少关闭支持而导致插头”。


5
2017-09-10 21:51



很好找。至少我知道为什么现在发生了。 - hross
深潜+1 - sunleo


我花了一些时间来弄清楚,但基本上需要做的是相应地创建CacheManager。

如果您创建缓存管理器和缓存的方式与在xml中创建它的方式相同,它将起作用。

net.sf.ehcache.CacheManager manager = net.sf.ehcache.CacheManager
        .create(new Configuration().diskStore(
            new DiskStoreConfiguration().path("C:/mycache")
        )
        .cache(new CacheConfiguration()
            .name(testName)
            .eternal(true)
            .maxBytesLocalHeap(10000, MemoryUnit.BYTES)
            .maxBytesLocalDisk(1000000, MemoryUnit.BYTES)
            .diskExpiryThreadIntervalSeconds(0)
            .diskPersistent(true)));

3
2017-11-04 15:56



我不认为这个答案适用。此问题特别是关于不持久的磁盘持久性缓存 第二 他们被关闭后启动。 - hross


这可能有点晚了,但我遇到了同样的问题:关闭缓存管理器的原因是什么。

(来自文件: http://ehcache.org/documentation/code-samples#ways-of-loading-cache-configuration

关闭单例CacheManager:

CacheManager.getInstance().shutdown();

关闭CacheManager实例,假设您有一个名为CacheManager的引用:

manager.shutdown();

2
2017-12-27 11:03



是的,我是第二个。但是,如果应用程序突然终止,则无法始终关闭缓存管理器。我们最终做的是启动一个调用高速缓存上的flush()方法的调度线程。不要使用非持久性缓存,因为它会清除它们。 - loopkin


我想你应该删除 manager.cacheExists(..) 测试并简单地使用创建缓存 testCache = manager.getCache("test"); 而不是使用 new Cache(..)。即使您的缓存是diskPersistent,它也不会存在,直到您第一次获得它。 (至少那是我认为的,因为我只是在使用它 getCache(..)它完全符合您的要求)

注意:

您还可以添加类似的内容以确保缓存存在:

Cache cache = manager.getCache(name);
if (cache == null) {
    throw new NullPointerException(String.format("no cache with name %s defined, please configure it in %s", name, url));
}

笔记2:

如果您的配置文件名为ehcache.xml,则不应使用 CacheManager.create(url)。而是使用CacheManager单例: 我觉得我很困惑 CacheManager.create(url) 使用和使用 new CacheManager(url)。不过,你应该使用单身人士 ehcache.xml 和 new CacheManager(url) 除了别的什么。

// ehcache.xml - shared between different invocations
CacheManager defaultManager = CacheManager.getInstance();
// others - avoid calling twice with same argument
CacheManager manager = CacheManager.create(url);

运用 CacheManager.create(..) 是有问题的 它可能会完全忽略传递的URL 如果有的话 create(..) 方法或 getInstance() 以前被称为:

public static CacheManager create(URL configurationFileURL) throws CacheException {
    synchronized (CacheManager.class) {
        if (singleton == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Creating new CacheManager with config URL: " + configurationFileURL);
            }
            singleton = new CacheManager(configurationFileURL);

        }
        return singleton;
    }
}

这就是为什么我不建议使用任何一个 CacheManager.create(..) 方法。使用 CacheManager.getInstance() 要么 new CacheManager(url)


1
2017-11-13 14:56



如果 manager.getCache("test")没有回来 null, 然后 manager.cacheExists(..) 应该 true。 - Pascal Thivent
@Pascal我刚刚查看了代码,你说得对。然而,检查不应该是必要的。所以@skaffman可能是对的,并且ehcache.xml找不到正确的位置。 - sfussenegger
OP正在以编程方式创建缓存,所以对我来说,检查完全正常(第一次,缓存不存在)。然后,关于你的注释2,使用的问题是什么 CacheManager.create(url) 即使调用配置文件 ehcache.xml (顺便说一句, ehcache.xml 是不是位于类路径的根部)? - Pascal Thivent
当我将配置文件的位置更改为无效时,我在控制台输出上收到警告,所以我很确定一切都是犹太洁食。当我省略检查以查看缓存是否存在时,当我尝试从中加载一个值时,我得到一个空指针异常(经理说第二次复飞时有0个缓存)。也许我需要在配置文件中而不是以编程方式创建缓存? - hross
@Pascal只要ehache与配置文件一起使用,以编程方式创建Cache就是问题本身。这应留给CacheManager(以及它从给定文件中读取的配置,而不是在代码和ehcache.xml中具有不同的配置)。所以OP应该 要么使用CacheManager,要么以编程方式创建Caches。 @hross看到我上面的编辑:尝试使用 new CacheManager(url) 代替。也许已经使用另一个URL创建了单例CacheManager。 - sfussenegger


如果磁盘上的缓存保持空白,则提示小提示:确保缓存中的元素是可序列化的。 ehcache会记录,如果不是这样,但我的日志设置没有打印出这些日志条目。


1
2018-06-14 10:12





我有并解决了类似的问题。

我想配置ehcache以在磁盘上具有给定的缓存持久化元素。 但我想只在当地环境中这样做(生产环境适用于 distributed 持久性)所以我在应用程序启动时以编程方式切换配置(在我的情况下是一个Web应用程序)

File configurationFile = new File(event.getServletContext().getRealPath(EHCACHE_CONFIG_PATH));    
Configuration configuration = ConfigurationFactory.parseConfiguration(configurationFile);

//...doing other stuff here...

CacheConfiguration cacheConfiguration = configuration.getCacheConfigurations().get("mycachename");
if(localEnvironment){    
    cacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.DISTRIBUTED));
}else{
    //siteCacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.LOCALRESTARTABLE));
    //deprecated lines..
    siteCacheConfiguration.setDiskPersistent(true);
    siteCacheConfiguration.setOverflowToDisk(true);
}

我对注释行有问题 siteCacheConfiguration.addPersistence(new PersistenceConfiguration().strategy(Strategy.LOCALRESTARTABLE)),实际上是Ehcache代码(我正在使用 ehcache-2.6.11如果你使用,则抛出异常 Strategy.LOCALRESTARTABLE 没有企业版的jar:

CacheException: You must use an enterprise version of Ehcache to successfully enable enterprise persistence.

深入研究代码,我意识到这两条(不推荐使用的)行做同样的事情,包括企业版Exception

siteCacheConfiguration.setDiskPersistent(true);
siteCacheConfiguration.setOverflowToDisk(true);

记得加 CacheManager.getInstance().shutdown() 关闭应用程序!

希望这可以帮助。


1
2018-02-09 15:31



这通常很好,除非您的应用程序/进程被具有SIGKILL的操作系统杀死。在这种情况下,将不执行关闭挂钩,并且很可能您的持久缓存文件(foo.data和foo.index)将被破坏,并且在启动时不会再次填充缓存。相反,它们将被丢弃并删除,并将丢失持久信息。显式调用CacheManager.getCache(“foo”)。flush()可以帮助缓解这个问题。 - ibai


我想这会起作用,但我仍然想知道为什么编程定义的缓存不能保留在磁盘上(特别是因为它们仍然写入磁盘!)

我的理解是以编程方式创建的缓存(即未在中声明) ehcache.xml)可以使用 DiskStore 它本身可以是持久的,但这并不意味着该缓存将由自动加载 CacheManager uppon重启。实际上,我不认为前面提到的文件确实包含缓存参数。

但是,如果使用相同的参数以编程方式“重新创建”缓存,则可以从中找到之前缓存的条目 DiskStore


0
2017-11-13 16:52