ehcache сохраняются к проблемам с диском - PullRequest
25 голосов
/ 13 ноября 2009

Я хочу сделать что-то с ehcache в Java, что, на мой взгляд, должно быть предельно простым, но я потратил достаточно времени, расстраивая себя документами ...

  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

Я, должно быть, делаю что-то глупое здесь, но я не уверен, что!

Ответы [ 8 ]

16 голосов
/ 13 ноября 2009

Хорошо, хорошо, что я сделал, чтобы исправить это, настроил мой кеш, используя файл конфигурации. Вот обновленный конфиг:

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

Так что в основном я не использовал конструктор для определения кэша.

Полагаю, это сработает, но я все еще удивляюсь, почему программно определенные кэши не могут сохраняться на диске (тем более что они все еще записываются на диск!).

Спасибо за комментарии, ребята.

5 голосов
/ 11 сентября 2010

Проведя некоторое время с отладчиком, я думаю, что у меня есть ответ для 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 локально.

Одно предостережение в том, что для некластеризованных кэшей мне необходимо выполнять flush () после каждого put () и remove (), чтобы сохранить образ диска свежим, особенно при отладке из-за отсутствия поддержки выключения, когда вы просто "потяните за вилку".

3 голосов
/ 04 ноября 2014

Мне потребовалось некоторое время, чтобы понять, но в основном здесь необходимо создать 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)));
2 голосов
/ 27 декабря 2012

это может быть немного поздно, но у меня была та же проблема: что помогло отключить диспетчер кэша.

(из документа: http://ehcache.org/documentation/code-samples#ways-of-loading-cache-configuration)

Отключение одиночного CacheManager:

CacheManager.getInstance().shutdown();

Завершите работу экземпляра CacheManager, если у вас есть ссылка на CacheManager с именем:

manager.shutdown();
1 голос
/ 09 февраля 2017

У меня была и решена похожая проблема.

Я хочу настроить ehcache так, чтобы на диске были постоянные элементы данного кэша. Но я хочу сделать это только в локальной среде (производственная среда работает с постоянством distributed), поэтому я переключаю конфигурацию программно при запуске приложения (веб-приложение в моем случае)

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 без корпоративной версии фляги:

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

Копаясь в коде, я понял, что эти две (устаревшие) строки делают то же самое, исключая версию Entreprise Exception

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

Не забудьте добавить CacheManager.getInstance().shutdown() при завершении работы приложения!

Надеюсь, это поможет.

1 голос
/ 14 июня 2012

Небольшая подсказка, если ваш кеш на диске остается пустым: убедитесь, что ваши элементы в кеше сериализуемы. ehcache ведет журнал, если это не так, но мои настройки журнала не распечатывали эти записи журнала.

1 голос
/ 13 ноября 2009

Я думаю, вам следует удалить тест 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).

0 голосов
/ 13 ноября 2009

Полагаю, это будет работать, но я все еще удивляюсь, почему программно определенные кеши не могут сохраняться на диске (тем более что они все еще записываются на диск!)

Насколько я понимаю, программно созданный кеш (т. Е. Не объявленный в ehcache.xml) может использовать DiskStore, который сам по себе может быть постоянным, но это не означает, что этот кеш будет автоматически загружаться с помощью CacheManager uppon запустить снова. На самом деле, я не думаю, что ранее упомянутые файлы содержат параметры кэша.

Но, если вы "воссоздаете" кэш программным образом с теми же параметрами, вы найдете ранее кэшированные записи обратно из DiskStore.

...