web-dev-qa-db-ja.com

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

ここで何かばかげたことをしているに違いありませんが、私にはわかりません!

26
hross

さて、これを修正するために行ったのは、構成ファイルを使用してキャッシュを構成することでした。更新された設定は次のとおりです。

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

したがって、基本的にはキャッシュを定義するためにコンストラクタを使用しませんでした。

私はこれはうまくいくと思いますが、プログラムで定義されたキャッシュがディスク上に永続化できない理由をまだ疑問に思っています(特に、それらはまだディスクに書き込まれているためです!)。

コメントをくれてありがとう。

17
hross

デバッガである程度の時間を過ごした後、私は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をローカルで実行する必要はありません。

1つの注意点は、クラスター化されていないキャッシュの場合、ディスクイメージを最新に保つために、すべてのput()およびremove()の後に、flush()を実行する必要があることです。プラグ」。

5

これには少し時間がかかりましたが、基本的にここで行う必要があるのは、それに応じて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
Christian

これは少し遅れるかもしれませんが、同じ問題がありました。キャッシュマネージャーをシャットダウンするのに役立ちました。

(ドキュメントから: http://ehcache.org/documentation/code-samples#ways-of-loading-cache-configuration

シングルトンCacheManagerをシャットダウンします。

CacheManager.getInstance().shutdown();

CacheManagerへの参照が呼び出されていると想定して、CacheManagerインスタンスをシャットダウンします。

manager.shutdown();
2
fabian

同様の問題があり、解決しました。

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_を使用しています)は、jarのエンタープライズバージョンなしで_Strategy.LOCALRESTARTABLE_を使用すると例外をスローします。

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

コードを掘り下げて、これら2つの(非推奨)行が同じように起業家バージョンの例外を回避していることに気付きました

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

アプリケーションのシャットダウン時にCacheManager.getInstance().shutdown()を追加することを忘れないでください!

お役に立てれば。

1
fustaki

ディスク上のキャッシュが空のままの場合の小さなヒント:キャッシュ内の要素がシリアル化可能であることを確認してください。そうでない場合、ehcacheはログを記録しますが、私のログ設定はこれらのログエントリを出力しませんでした。

1
Bruno Eberhard

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
sfussenegger

私はこれはうまくいくと思いますが、プログラムで定義されたキャッシュがディスク上に永続化できない理由をまだ疑問に思っています(特に、それらはまだディスクに書き込まれているためです!)

私の理解では、プログラムで作成されたキャッシュ(つまり、ehcache.xmlで宣言されていない)はDiskStoreを使用でき、それ自体は永続的ですが、これは、このキャッシュがCacheManager uppon再起動。実際、前述のファイルにはキャッシュパラメータが含まれていないと思います。

ただし、同じパラメータを使用してプログラムでキャッシュを「再作成」すると、以前にキャッシュされたエントリがDiskStoreから戻されます。

0
Pascal Thivent